From 5a4683962b390e5f60bb3ca7437c505999e8dded Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Mon, 29 Mar 2021 13:34:26 +0200 Subject: [PATCH 001/683] [core] Extended logs for negative or zero RTT estimate on the receiver side (#1876) * Minor refactoring to processCtrl function, UMSG_ACKACK * Minor formatting in CACKWindow::acknowledge function * The time of ACKACK reception is now passed as an argument to the CACKWindow::acknowledge function --- srtcore/core.cpp | 33 +++++++-- srtcore/core.h | 170 ++++++++++++++++++++++----------------------- srtcore/window.cpp | 26 +++---- srtcore/window.h | 62 +++++++++-------- 4 files changed, 157 insertions(+), 134 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c31b7088a..5a1c62209 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8198,19 +8198,40 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement { int32_t ack = 0; - const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack); + + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, currtime); + + if (rtt == -1) + { + if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) + { + LOGC(inlog.Warn, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + break; + } + + LOGC(inlog.Error, + log << CONID() << "IPE: ACK record not found, can't estimate RTT " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + break; + } + if (rtt <= 0) { LOGC(inlog.Error, - log << CONID() << "IPE: ACK node overwritten when acknowledging " << ctrlpkt.getAckSeqNo() - << " (ack extracted: " << ack << ")"); + log << CONID() << "IPE: invalid RTT estimate " << rtt + << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); break; } - // if increasing delay detected... + // If increasing delay is detected // sendCtrl(UMSG_CGWARNING); - // RTT EWMA + // Calculate RTT (EWMA) on the receiver side m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); m_iRTT = avg_iir<8>(m_iRTT, rtt); @@ -8240,7 +8261,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) #endif } - // update last ACK that has been received by the sender + // Update last ACK that has been received by the sender if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) m_iRcvLastAckAck = ack; diff --git a/srtcore/core.h b/srtcore/core.h index 0a4c2d86d..9ee3df6d7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -259,23 +259,22 @@ class CUDT } }; - static const SRTSOCKET INVALID_SOCK = -1; // invalid socket descriptor - static const int ERROR = -1; // socket api error returned value + static const SRTSOCKET INVALID_SOCK = -1; // Invalid socket descriptor + static const int ERROR = -1; // Socket api error returned value static const int HS_VERSION_UDT4 = 4; static const int HS_VERSION_SRT1 = 5; // Parameters // - // Note: use notation with X*1000*1000* ... instead of million zeros in a row. - // In C++17 there is a possible notation of 5'000'000 for convenience, but that's - // something only for a far future. - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; + // Note: use notation with X*1000*1000*... instead of million zeros in a row + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; int handshakeVersion() { @@ -295,37 +294,38 @@ class CUDT SRTSOCKET socketID() const { return m_SocketID; } - static CUDT* getUDTHandle(SRTSOCKET u); - static std::vector existingSockets(); + static CUDT* getUDTHandle(SRTSOCKET u); + static std::vector existingSockets(); void addressAndSend(CPacket& pkt); void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); - bool isOPT_TsbPd() const { return m_config.bTSBPD; } - int RTT() const { return m_iRTT; } - int RTTVar() const { return m_iRTTVar; } - int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } - int32_t schedSeqNo() const { return m_iSndNextSeqNo; } - bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } - srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } - - int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } - int flowWindowSize() const { return m_iFlowWindowSize; } - int32_t deliveryRate() const { return m_iDeliveryRate; } - int bandwidth() const { return m_iBandwidth; } - int64_t maxBandwidth() const { return m_config.llMaxBW; } - int MSS() const { return m_config.iMSS; } - - uint32_t peerLatency_us() const {return m_iPeerTsbPdDelay_ms * 1000; } - int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout; } - size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } - size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } - int sndLossLength() { return m_pSndLossList->getLossLength(); } - int32_t ISN() const { return m_iISN; } - int32_t peerISN() const { return m_iPeerISN; } - duration minNAKInterval() const { return m_tdMinNakInterval; } - sockaddr_any peerAddr() const { return m_PeerAddr; } + bool isOPT_TsbPd() const { return m_config.bTSBPD; } + int RTT() const { return m_iRTT; } + int RTTVar() const { return m_iRTTVar; } + int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } + int32_t schedSeqNo() const { return m_iSndNextSeqNo; } + bool overrideSndSeqNo(int32_t seq); + + srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } + srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } + + int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + int flowWindowSize() const { return m_iFlowWindowSize; } + int32_t deliveryRate() const { return m_iDeliveryRate; } + int bandwidth() const { return m_iBandwidth; } + int64_t maxBandwidth() const { return m_config.llMaxBW; } + int MSS() const { return m_config.iMSS; } + + uint32_t peerLatency_us() const { return m_iPeerTsbPdDelay_ms * 1000; } + int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout; } + size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } + size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } + int sndLossLength() { return m_pSndLossList->getLossLength(); } + int32_t ISN() const { return m_iISN; } + int32_t peerISN() const { return m_iPeerISN; } + duration minNAKInterval() const { return m_tdMinNakInterval; } + sockaddr_any peerAddr() const { return m_PeerAddr; } /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @param lastack is the sequence number of the first unacknowledged packet. @@ -691,28 +691,28 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - static CUDTUnited s_UDTUnited; // UDT global management base + static CUDTUnited s_UDTUnited; // UDT global management base private: // Identification - CUDTSocket* const m_parent; // temporary, until the CUDTSocket class is merged with CUDT - SRTSOCKET m_SocketID; // UDT socket number - SRTSOCKET m_PeerID; // peer id, for multiplexer + CUDTSocket* const m_parent; // Temporary, until the CUDTSocket class is merged with CUDT + SRTSOCKET m_SocketID; // UDT socket number + SRTSOCKET m_PeerID; // Peer ID, for multiplexer // HSv4 (legacy handshake) support) - time_point m_tsSndHsLastTime; //Last SRT handshake request time - int m_iSndHsRetryCnt; //SRT handshake retries left + time_point m_tsSndHsLastTime; // Last SRT handshake request time + int m_iSndHsRetryCnt; // SRT handshake retries left #if ENABLE_EXPERIMENTAL_BONDING - SRT_GROUP_TYPE m_HSGroupType; // group type about-to-be-set in the handshake + SRT_GROUP_TYPE m_HSGroupType; // Group type about-to-be-set in the handshake #endif private: - int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes - int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds - int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds - bool m_bTLPktDrop; // Enable Too-late Packet Drop - UniquePtr m_pCryptoControl; // congestion control SRT class (small data extension) - CCache* m_pCache; // network information cache + int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes + int m_iTsbPdDelay_ms; // Rx delay to absorb burst, in milliseconds + int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst, in milliseconds + bool m_bTLPktDrop; // Enable Too-late Packet Drop + UniquePtr m_pCryptoControl; // Congestion control SRT class (small data extension) + CCache* m_pCache; // Network information cache // Congestion control std::vector m_Slots[TEV_E_SIZE]; @@ -727,7 +727,7 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - volatile bool m_bListening; // If the UDT entit is listening to connection + volatile bool m_bListening; // If the UDT entity is listening to connection volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed volatile bool m_bConnected; // Whether the connection is on or off volatile bool m_bClosing; // If the UDT entity is closing @@ -736,7 +736,7 @@ class CUDT volatile bool m_bPeerHealth; // If the peer status is normal volatile int m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected + int m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second @@ -746,8 +746,8 @@ class CUDT int m_iByteDeliveryRate; // Byte arrival rate at the receiver side - CHandShake m_ConnReq; // connection request - CHandShake m_ConnRes; // connection response + CHandShake m_ConnReq; // Connection request + CHandShake m_ConnRes; // Connection response CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state HandshakeSide m_SrtHsSide; // HSv5 rendezvous handshake side resolved from cookie contest (DRAW if not yet resolved) @@ -758,32 +758,32 @@ class CUDT /*volatile*/ duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles - /*volatile*/ duration m_tdSendTimeDiff; // aggregate difference in inter-packet sending time + /*volatile*/ duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time volatile int m_iFlowWindowSize; // Flow control window size - volatile double m_dCongestionWindow; // congestion window size + volatile double m_dCongestionWindow; // Congestion window size private: // Timers - /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below - /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time - - /*volatile*/ duration m_tdACKInterval; // ACK interval - /*volatile*/ duration m_tdNAKInterval; // NAK interval - /*volatile*/ time_point m_tsLastRspTime; // time stamp of last response from the peer - /*volatile*/ time_point m_tsLastRspAckTime; // time stamp of last ACK from the peer - /*volatile*/ time_point m_tsLastSndTime; // time stamp of last data/ctrl sent (in system ticks) - time_point m_tsLastWarningTime; // Last time that a warning message is sent - time_point m_tsLastReqTime; // last time when a connection request is sent + /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below + /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time + + /*volatile*/ duration m_tdACKInterval; // ACK interval + /*volatile*/ duration m_tdNAKInterval; // NAK interval + /*volatile*/ time_point m_tsLastRspTime; // Timestamp of last response from the peer + /*volatile*/ time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer + /*volatile*/ time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) + time_point m_tsLastWarningTime; // Last time that a warning message is sent + time_point m_tsLastReqTime; // last time when a connection request is sent time_point m_tsRcvPeerStartTime; - time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) - time_point m_tsLastAckTime; // Timestamp of last ACK - duration m_tdMinNakInterval; // NAK timeout lower bound; too small value can cause unnecessary retransmission - duration m_tdMinExpInterval; // timeout lower bound threshold: too small timeout can cause problem + time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) + time_point m_tsLastAckTime; // Timestamp of last ACK + duration m_tdMinNakInterval; // NAK timeout lower bound; too small value can cause unnecessary retransmission + duration m_tdMinExpInterval; // Timeout lower bound threshold: too small timeout can cause problem - int m_iPktCount; // packet counter for ACK - int m_iLightACKCount; // light ACK counter + int m_iPktCount; // Packet counter for ACK + int m_iLightACKCount; // Light ACK counter - time_point m_tsNextSendTime; // scheduled time of next packet sending + time_point m_tsNextSendTime; // Scheduled time of next packet sending volatile int32_t m_iSndLastFullAck; // Last full ACK received volatile int32_t m_iSndLastAck; // Last ACK received @@ -843,17 +843,17 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data - CRcvBuffer* m_pRcvBuffer; //< Receiver buffer - CRcvLossList* m_pRcvLossList; //< Receiver loss list - std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. - int m_iReorderTolerance; //< Current value of dynamic reorder tolerance - int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_FreshLoss; // Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; // Current value of dynamic reorder tolerance + int m_iConsecEarlyDelivery; // Increases with every OOO packet that came m_ACKWindow; //< ACK history window - CPktTimeWindow<16, 64> m_RcvTimeWindow; //< Packet arrival time window + CACKWindow m_ACKWindow; // ACK history window + CPktTimeWindow<16, 64> m_RcvTimeWindow; // Packet arrival time window - int32_t m_iRcvLastAck; //< Last sent ACK + int32_t m_iRcvLastAck; // Last sent ACK #ifdef ENABLE_LOGGING int32_t m_iDebugPrevLastAck; #endif @@ -869,10 +869,10 @@ class CUDT uint32_t m_uPeerSrtFlags; bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets - bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead. + bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead srt::sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle - srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock. + srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent srt::sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining diff --git a/srtcore/window.cpp b/srtcore/window.cpp index 0c40af128..ea8386fe1 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -77,22 +77,21 @@ void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t s r_iTail = (r_iTail + 1) % size; } -int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack) +int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const steady_clock::time_point& currtime) { + // Head has not exceeded the physical boundary of the window if (r_iHead >= r_iTail) { - // Head has not exceeded the physical boundary of the window - for (int i = r_iTail, n = r_iHead; i < n; ++ i) { - // looking for identical ACK Seq. No. + // Looking for an identical ACK Seq. No. if (seq == r_aSeq[i].iACKSeqNo) { - // return the Data ACK it carried + // Return the Data ACK it carried r_ack = r_aSeq[i].iACK; - // calculate RTT - const int rtt = count_microseconds(steady_clock::now() - r_aSeq[i].tsTimeStamp); + // Calculate RTT estimate + const int rtt = count_microseconds(currtime - r_aSeq[i].tsTimeStamp); if (i + 1 == r_iHead) { @@ -106,22 +105,22 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 } } - // Bad input, the ACK node has been overwritten + // The record about ACK is not found in the buffer, RTT can not be calculated return -1; } // Head has exceeded the physical window boundary, so it is behind tail for (int j = r_iTail, n = r_iHead + size; j < n; ++ j) { - // looking for indentical ACK seq. no. + // Looking for an identical ACK Seq. No. if (seq == r_aSeq[j % size].iACKSeqNo) { - // return Data ACK + // Return the Data ACK it carried j %= size; r_ack = r_aSeq[j].iACK; - // calculate RTT - const int rtt = count_microseconds(steady_clock::now() - r_aSeq[j].tsTimeStamp); + // Calculate RTT estimate + const int rtt = count_microseconds(currtime - r_aSeq[j].tsTimeStamp); if (j == r_iHead) { @@ -135,9 +134,10 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 } } - // bad input, the ACK node has been overwritten + // The record about ACK is not found in the buffer, RTT can not be calculated return -1; } + } //////////////////////////////////////////////////////////////////////////////// diff --git a/srtcore/window.h b/srtcore/window.h index b3baf0d60..b7f510163 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -58,20 +58,20 @@ modified by #include #include #endif -#include "udt.h" #include "packet.h" +#include "udt.h" namespace ACKWindowTools { struct Seq { - int32_t iACKSeqNo; // Seq. No. for the ACK packet - int32_t iACK; // Data Seq. No. carried by the ACK packet - srt::sync::steady_clock::time_point tsTimeStamp; // The timestamp when the ACK was sent + int32_t iACKSeqNo; // Seq. No. of the ACK packet + int32_t iACK; // Data packet Seq. No. carried by the ACK packet + srt::sync::steady_clock::time_point tsTimeStamp; // The timestamp when the ACK was sent }; void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack); - int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack); + int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const srt::sync::steady_clock::time_point& currtime); } template @@ -89,22 +89,24 @@ class CACKWindow ~CACKWindow() {} /// Write an ACK record into the window. - /// @param [in] seq ACK seq. no. - /// @param [in] ack DATA ACK no. + /// @param [in] seq Seq. No. of the ACK packet + /// @param [in] ack Data packet Seq. No. carried by the ACK packet void store(int32_t seq, int32_t ack) { return ACKWindowTools::store(m_aSeq, SIZE, m_iHead, m_iTail, seq, ack); } - /// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT . - /// @param [in] seq ACK-2 seq. no. - /// @param [out] ack the DATA ACK no. that matches the ACK-2 no. - /// @return RTT. + /// Search the ACKACK "seq" in the window, find out the data packet "ack" + /// and calculate RTT estimate based on the ACK/ACKACK pair + /// @param [in] seq Seq. No. of the ACK packet carried within ACKACK + /// @param [out] ack Acknowledged data packet Seq. No. from the ACK packet that matches the ACKACK + /// @param [in] currtime The timestamp of ACKACK packet reception by the receiver + /// @return RTT - int acknowledge(int32_t seq, int32_t& r_ack) + int acknowledge(int32_t seq, int32_t& r_ack, const srt::sync::steady_clock::time_point& currtime) { - return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack); + return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack, currtime); } private: @@ -112,7 +114,7 @@ class CACKWindow typedef ACKWindowTools::Seq Seq; Seq m_aSeq[SIZE]; - int m_iHead; // Pointer to the lastest ACK record + int m_iHead; // Pointer to the latest ACK record int m_iTail; // Pointer to the oldest ACK record private: @@ -323,22 +325,22 @@ class CPktTimeWindow: CPktTimeWindowTools } private: - int m_aPktWindow[ASIZE]; // packet information window (inter-packet time) - int m_aBytesWindow[ASIZE]; // - int m_iPktWindowPtr; // position pointer of the packet info. window. - mutable srt::sync::Mutex m_lockPktWindow; // used to synchronize access to the packet window - - int m_aProbeWindow[PSIZE]; // record inter-packet time for probing packet pairs - int m_iProbeWindowPtr; // position pointer to the probing window - mutable srt::sync::Mutex m_lockProbeWindow; // used to synchronize access to the probe window - - int m_iLastSentTime; // last packet sending time - int m_iMinPktSndInt; // Minimum packet sending interval - - srt::sync::steady_clock::time_point m_tsLastArrTime; // last packet arrival time - srt::sync::steady_clock::time_point m_tsCurrArrTime; // current packet arrival time - srt::sync::steady_clock::time_point m_tsProbeTime; // arrival time of the first probing packet - int32_t m_Probe1Sequence; // sequence number for which the arrival time was notified + int m_aPktWindow[ASIZE]; // Packet information window (inter-packet time) + int m_aBytesWindow[ASIZE]; + int m_iPktWindowPtr; // Position pointer of the packet info. window + mutable srt::sync::Mutex m_lockPktWindow; // Used to synchronize access to the packet window + + int m_aProbeWindow[PSIZE]; // Record inter-packet time for probing packet pairs + int m_iProbeWindowPtr; // Position pointer to the probing window + mutable srt::sync::Mutex m_lockProbeWindow; // Used to synchronize access to the probe window + + int m_iLastSentTime; // Last packet sending time + int m_iMinPktSndInt; // Minimum packet sending interval + + srt::sync::steady_clock::time_point m_tsLastArrTime; // Last packet arrival time + srt::sync::steady_clock::time_point m_tsCurrArrTime; // Current packet arrival time + srt::sync::steady_clock::time_point m_tsProbeTime; // Arrival time of the first probing packet + int32_t m_Probe1Sequence; // Sequence number for which the arrival time was notified private: CPktTimeWindow(const CPktTimeWindow&); From 0e7185e84080ae40bab639226a36dc7510001506 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Mon, 29 Mar 2021 13:35:17 +0200 Subject: [PATCH 002/683] [docs] Moved API docs in a separate folder, improved API documentation (#1893) --- README.md | 13 +- docs/{ => API}/API-functions.md | 500 +++++++++--------- .../API-socket-options.md} | 31 +- docs/{ => API}/API.md | 83 ++- docs/{ => API}/statistics.md | 54 +- docs/AccessControl.md | 2 +- docs/README.md | 10 + docs/handshake.md | 2 +- 8 files changed, 342 insertions(+), 353 deletions(-) rename docs/{ => API}/API-functions.md (94%) rename docs/{APISocketOptions.md => API/API-socket-options.md} (98%) rename docs/{ => API}/API.md (93%) rename docs/{ => API}/statistics.md (93%) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 809a2d56f..eda49484e 100644 --- a/README.md +++ b/README.md @@ -33,16 +33,15 @@ As audio/video packets are streamed from a source to a destination device, SRT d ### Guides -* [Why SRT Was Created](docs/why-srt-was-created.md) -* [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) -* SRT Cookbook: [website](https://srtlab.github.io/srt-cookbook), [GitHub](https://github.com/SRTLab/srt-cookbook) -* SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) +* [SRT API Documents](docs/API/) * [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) -* [Contributing](CONTRIBUTING.md) * [Developer's Guide](docs/DevelopersGuide.md) -* [SRT Encryption](docs/encryption.md) -* [API](docs/API.md) +* [Contributing](CONTRIBUTING.md) * [Reporting problems](docs/reporting.md) +* SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) +* SRT CookBook: [Website](https://srtlab.github.io/srt-cookbook), [GitHub Repo](https://github.com/SRTLab/srt-cookbook) +* [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) +* [Why SRT Was Created](docs/why-srt-was-created.md) ## Requirements diff --git a/docs/API-functions.md b/docs/API/API-functions.md similarity index 94% rename from docs/API-functions.md rename to docs/API/API-functions.md index e00e2e74f..9ed45dc7b 100644 --- a/docs/API-functions.md +++ b/docs/API/API-functions.md @@ -99,7 +99,7 @@ | [srt_bistats](#srt_bistats) | Reports the current statistics | | | | -

Asynchronous Operations (epoll)

+

Asynchronous Operations (Epoll)

| *Function / Structure* | *Description* | |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | @@ -166,8 +166,8 @@ | [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | Rendezvous cookie collision | | [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | Both parties have defined a passprhase for connection and they differ | | [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | Only one connection party has set up a password | -| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | The value for [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) flag is different on both connection parties | -| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | The [`SRTO_PACKETFILTER`](../docs/APISocketOptions.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | +| [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | The value for [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties | +| [SRT_REJ_FILTER](#SRT_REJ_FILTER) | The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | The group type or some group settings are incompatible for both connection parties | | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | The connection wasn't rejected, but it timed out | | | | @@ -215,13 +215,13 @@ [`SRT_EASYNCSND`](#srt_easyncsnd) | Sending operation is not ready to perform | [`SRT_EASYNCRCV`](#srt_easyncrcv) | Receiving operation is not ready to perform | [`SRT_ETIMEOUT`](#srt_etimeout) | The operation timed out | -[`SRT_ECONGEST`](#srt_econgest) | With [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) set to true,
some packets were dropped by sender | +[`SRT_ECONGEST`](#srt_econgest) | With [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) set to true,
some packets were dropped by sender | [`SRT_EPEERERR`](#srt_epeererr) | Receiver peer is writing to a file that the agent is sending | | | | -## Library initialization +## Library Initialization * [srt_startup](#srt_startup) * [srt_cleanup](#srt_cleanup) @@ -275,7 +275,14 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne `srt_cleanup` function exactly the same number of times. -## Creating and configuring sockets +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + + +## Creating and Configuring Sockets * [srt_socket](#srt_socket) * [srt_create_socket](#srt_create_socket) @@ -303,9 +310,9 @@ is decided at the call of [`srt_connect`](#srt_connect) or [`srt_bind`](#srt_bin using `SOCK_STREAM` or `SOCK_DGRAM` symbols (with the latter being misleading, as the message mode has nothing to do with UDP datagrams and it's rather similar to the SCTP protocol). In SRT these two modes are available by setting -[`SRTO_TRANSTYPE`](../docs/APISocketOptions.md#SRTO_TRANSTYPE). The default is `SRTT_LIVE`. If, however, you set -[`SRTO_TRANSTYPE`](../docs/APISocketOptions.md#SRTO_TRANSTYPE) to `SRTT_FILE` for file mode, you can then leave the -[`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) option as false (default), which corresponds to "stream" mode +[`SRTO_TRANSTYPE`](API-socket-options.md#SRTO_TRANSTYPE). The default is `SRTT_LIVE`. If, however, you set +[`SRTO_TRANSTYPE`](API-socket-options.md#SRTO_TRANSTYPE) to `SRTT_FILE` for file mode, you can then leave the +[`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) option as false (default), which corresponds to "stream" mode (TCP-like), or set it to true, which corresponds to "message" mode (SCTP-like). @@ -356,14 +363,14 @@ This call is obligatory for a listening socket before calling [`srt_listen`](#sr and for rendezvous mode before calling [`srt_connect`](#srt_connect); otherwise it's optional. For a listening socket it defines the network interface and the port where the listener should expect a call request. In the case of rendezvous mode (when the -socket has set [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) to +socket has set [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) to true both parties connect to one another) it defines the network interface and port from which packets will be sent to the peer, and the port to which the peer is expected to send packets. For a connecting socket this call can set up the outgoing port to be used in the communication. It is allowed that multiple SRT sockets share one local outgoing -port, as long as [`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) +port, as long as [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to *true* (default). Without this call the port will be automatically selected by the system. @@ -385,8 +392,6 @@ connecting, use [`srt_connect_bind`](#srt_connect_bind) for that purpose. | | | - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -400,7 +405,6 @@ int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); A version of [`srt_bind`](#srt_bind) that acquires a given UDP socket instead of creating one. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -427,7 +431,6 @@ Gets the current status of the socket. Possible states are: | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -450,7 +453,6 @@ This function can be used for diagnostics. It is especially useful when the socket needs to be closed asynchronously. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -476,6 +478,12 @@ last user closed. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + ## Connecting @@ -503,7 +511,7 @@ socket and the [`srt_accept`](#srt_accept) function: * [`srt_listen_callback`](#srt_listen_callback) installs a user function that will be called before [`srt_accept`](#srt_accept) can happen -* [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) option allows +* [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) option allows the listener socket to accept group connections | Returns | | @@ -516,14 +524,13 @@ the listener socket to accept group connections | [`SRT_EINVPARAM`](#srt_einvparam) | Value of `backlog` is 0 or negative. | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid SRT socket. | | [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | [`srt_bind`](#srt_bind) has not yet been called on that socket. | -| [`SRT_ERDVNOSERV`](#srt_erdvnoserv) | [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) flag is set to true on specified socket. | +| [`SRT_ERDVNOSERV`](#srt_erdvnoserv) | [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) flag is set to true on specified socket. | | [`SRT_EINVOP`](#srt_einvop) | Internal error (should not happen when [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) is reported). | | [`SRT_ECONNSOCK`](#srt_econnsock) | The socket is already connected. | -| [`SRT_EDUPLISTEN`](#srt_eduplisten) | The address used in [`srt_bind`](#srt_bind) by this socket is already occupied by another listening socket.
Binding multiple sockets to one IP address and port is allowed, as long as
[`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) is set to true, but only one of these sockets can be set up as a listener. | +| [`SRT_EDUPLISTEN`](#srt_eduplisten) | The address used in [`srt_bind`](#srt_bind) by this socket is already occupied by another listening socket.
Binding multiple sockets to one IP address and port is allowed, as long as
[`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to true, but only one of these sockets can be set up as a listener. | | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -554,14 +561,14 @@ information through [`srt_group_data`](#srt_group_data) or the data filled by [`srt_sendmsg2`](#srt_sendmsg) and [`srt_recvmsg2`](#srt_recvmsg2). If the `lsn` listener socket is configured for blocking mode -([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) set to true, default), +([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) set to true, default), the call will block until the incoming connection is ready. Otherwise, the call always returns immediately. The `SRT_EPOLL_IN` epoll event should be checked on the `lsn` socket prior to calling this function in that case. If the pending connection is a group connection (initiated on the peer side by calling the connection function using a group ID, and permitted on the listener -socket by the [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) +socket by the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag), then the value returned is a group ID. This function then creates a new group, as well as a new socket for this connection, that will be added to the group. Once the group is created this way, further connections within the same @@ -586,7 +593,6 @@ internal use only. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -642,7 +648,6 @@ calling this function. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -687,13 +692,13 @@ The callback function gets the following parameters passed: * `ns`: The freshly created socket to handle the incoming connection * `hs_version`: The handshake version (usually 5, pre-1.3 versions of SRT use 4) * `peeraddr`: The address of the incoming connection -* `streamid`: The value set to [`SRTO_STREAMID`](../docs/APISocketOptions.md#SRTO_STREAMID) option set on the peer side +* `streamid`: The value set to [`SRTO_STREAMID`](API-socket-options.md#SRTO_STREAMID) option set on the peer side Note that SRT versions that use handshake version 4 are incapable of using any extensions, such as `streamid`. However they do support encryption. Note also that the SRT version isn't extracted at this point. However you can prevent connections with versions that are too old by using the -[`SRTO_MINVERSION`](../docs/APISocketOptions.md#SRTO_MINVERSION) option. +[`SRTO_MINVERSION`](API-socket-options.md#SRTO_MINVERSION) option. The callback function is given an opportunity to: @@ -728,7 +733,6 @@ Avoid any extensive search operations. It is best to cache in memory whatever database you have to check against the data received in `streamid` or `peeraddr`. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -759,7 +763,7 @@ interfaces) and port 0 (which makes the system assign the port automatically). 2. This function is used for both connecting to the listening peer in a caller-listener arrangement, and calling the peer in rendezvous mode. For the latter, the -[`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) flag must be set +[`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) flag must be set to true prior to calling this function, and binding, as described in #1, is in this case obligatory (see `SRT_ERDVUNBOUND` below). @@ -784,12 +788,12 @@ This function also allows you to use additional settings, available only for gro | [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Socket [`u`](#u) is in rendezvous mode, but it wasn't bound (see note #2) | | [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | | [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | -| [`SRT_ENOSERVER`](#srt_enoserver) | Connection has been timed out (see [`SRTO_CONNTIMEO`](../docs/APISocketOptions.md#SRTO_CONNTIMEO)) | +| [`SRT_ENOSERVER`](#srt_enoserver) | Connection has been timed out (see [`SRTO_CONNTIMEO`](API-socket-options.md#SRTO_CONNTIMEO)) | | [`SRT_ESCLOSED`](#srt_esclosed) | The socket [`u`](#u) has been closed while the function was blocking the call | | | | If the `u` socket is configured for blocking mode (when -[`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to true, default), +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, default), the call will block until the connection succeeds or fails. The "early" errors [`SRT_EINVSOCK`](#srt_einvsock), [`SRT_ERDVUNBOUND`](#srt_erdvunbound) and [`SRT_ECONNSOCK`](#srt_econnsock) are reported in both modes immediately. Other @@ -808,7 +812,6 @@ mode a detailed "late" failure cannot be distinguished, and therefore it can also be obtained from this function. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -856,7 +859,6 @@ different families (that is, both `source` and `target` must be `AF_INET` or `AF_INET6`), although you may mix links over IPv4 and IPv6 in one group. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -873,7 +875,6 @@ specifying the Initial Sequence Number for data transmission. Normally this valu is generated randomly. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -884,7 +885,7 @@ int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_nam const struct sockaddr* remote_name, int remote_namelen); ``` Performs a rendezvous connection. This is a shortcut for doing bind locally, -setting the [`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) option +setting the [`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) option to true, and doing [`srt_connect`](#srt_connect). **Arguments**: @@ -913,7 +914,6 @@ to true, and doing [`srt_connect`](#srt_connect). allowed (that is, both `local_name` and `remote_name` must be `AF_INET` or `AF_INET6`). - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -927,7 +927,7 @@ This call installs a callback hook, which will be executed on a given [`u`](#u) socket or all member sockets of a [`u`](#u) group, just after a pending connection in the background has been resolved and the connection has failed. Note that this function is not guaranteed to be called if the [`u`](#u) socket is set to blocking -mode ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) option set to true). +mode ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) option set to true). It is guaranteed to be called when a socket is in non-blocking mode, or when you use a group. @@ -982,28 +982,20 @@ typedef void srt_connect_callback_fn(void* opaq, SRTSOCKET ns, int errorcode, co * `token`: The token value, if it was used for group connection, otherwise -1 -## Socket group management +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + + + +## Socket Group Management * [SRT_GROUP_TYPE](#SRT_GROUP_TYPE) * [SRT_SOCKGROUPCONFIG](#SRT_SOCKGROUPCONFIG) * [SRT_SOCKGROUPDATA](#SRT_SOCKGROUPDATA) * [SRT_MEMBERSTATUS](#SRT_MEMBERSTATUS) - -[Functions to be used on groups](#functions-to-be-used-on-groups): - - * [srt_create_group](#srt_create_group) - * [srt_include](#srt_include) - * [srt_exclude](#srt_exclude) - * [srt_groupof](#srt_groupof) - * [srt_group_data](#srt_group_data) - * [srt_connect_group](#srt_connect_group) - * [srt_prepare_endpoint](#srt_prepare_endpoint) - * [srt_create_config](#srt_create_config) - * [srt_delete_config](#srt_delete_config) - * [srt_config_add](#srt_config_add) - - ### SRT_GROUP_TYPE The following group types are collected in an [`SRT_GROUP_TYPE`](#SRT_GROUP_TYPE) enum: @@ -1142,11 +1134,24 @@ as the only active one, this link will be "silenced" (its state will become `SRT_GST_IDLE`). +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- -## Functions to be used on groups: +### Functions to Be Used on Groups -### srt_create_group + * [srt_create_group](#srt_create_group) + * [srt_include](#srt_include) + * [srt_exclude](#srt_exclude) + * [srt_groupof](#srt_groupof) + * [srt_group_data](#srt_group_data) + * [srt_connect_group](#srt_connect_group) + * [srt_prepare_endpoint](#srt_prepare_endpoint) + * [srt_create_config](#srt_create_config) + * [srt_delete_config](#srt_delete_config) + * [srt_config_add](#srt_config_add) + +#### srt_create_group ``` SRTSOCKET srt_create_group(SRT_GROUP_TYPE type); @@ -1162,7 +1167,7 @@ the `SRTGROUP_MASK` bit is set on it, unlike for socket ID. --- -### srt_include +#### srt_include ``` int srt_include(SRTSOCKET socket, SRTSOCKET group); @@ -1172,12 +1177,11 @@ This function adds a socket to a group. This is only allowed for unmanaged groups. No such group type is currently implemented. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_exclude +#### srt_exclude ``` int srt_exclude(SRTSOCKET socket); @@ -1187,12 +1191,11 @@ This is only allowed for unmanaged groups. No such group type is currently implemented. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_groupof +#### srt_groupof ``` SRTSOCKET srt_groupof(SRTSOCKET socket); @@ -1202,12 +1205,11 @@ Returns the group ID of the socket, or `SRT_INVALID_SOCK` if the socket doesn't exist or it's not a member of any group. - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_group_data +#### srt_group_data ``` int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); @@ -1239,14 +1241,13 @@ and providing `socketgroup` and `inoutlen`. | -1 | Error | | | | + | Errors | | |:---------------------------------- |:--------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | Reported if `socketgroup` is not an existing group ID | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Reported if `inoutlen` if less than the size of the group | | | | - - | in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | |:---------:|:--------------:|:------------:|:----------:|:------------:|:---------------------------------:| @@ -1257,12 +1258,11 @@ and providing `socketgroup` and `inoutlen`. | ptr | < group.size | -1 | ✖️ | group.size | [`SRT_ELARGEMSG`](#srt_elargemsg) | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_connect_group +#### srt_connect_group ``` int srt_connect_group(SRTSOCKET group, @@ -1276,7 +1276,7 @@ in `name` array. However if you did this in blocking mode, the first call to [`srt_connect`](#srt_connect) would block until the connection is established, whereas this function blocks until any of the specified connections is established. -If you set the group nonblocking mode ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) +If you set the group nonblocking mode ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) option), there's no difference, except that the [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure allows you to add extra configuration data used by groups. Note also that this function accepts only groups, not sockets. @@ -1367,13 +1367,11 @@ define a unique value for the `token`. Your application can also set unique valu in which case the `token` value will be preserved. - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- -### srt_prepare_endpoint +#### srt_prepare_endpoint ``` SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, @@ -1421,7 +1419,7 @@ the [`errorcode`](#error-codes) field. --- -### srt_create_config +#### srt_create_config ``` SRT_SOCKOPT_CONFIG* srt_create_config(); @@ -1443,7 +1441,7 @@ should delete it using [`srt_delete_config`](#srt_delete_config). --- -### srt_delete_config +#### srt_delete_config ``` void srt_delete_config(SRT_SOCKOPT_CONFIG* c); @@ -1456,7 +1454,7 @@ Deletes the configuration object. --- -### srt_config_add +#### srt_config_add ``` int srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); @@ -1476,16 +1474,16 @@ on every socket, you should instead set this option on the whole group. The following options are allowed to be set on the member socket: -* [`SRTO_SNDBUF`](../docs/APISocketOptions.md#SRTO_SNDBUF): Allows for larger sender buffer for slower links -* [`SRTO_RCVBUF`](../docs/APISocketOptions.md#SRTO_RCVBUF): Allows for larger receiver buffer for longer recovery -* [`SRTO_UDP_RCVBUF`](../docs/APISocketOptions.md#SRTO_UDP_RCVBUF): UDP receiver buffer, if this link has a big flight window -* [`SRTO_UDP_SNDBUF`](../docs/APISocketOptions.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window -* [`SRTO_SNDDROPDELAY`](../docs/APISocketOptions.md#SRTO_SNDDROPDELAY): When particular link tends to drop too eagerly -* [`SRTO_NAKREPORT`](../docs/APISocketOptions.md#SRTO_NAKREPORT): If you don't want NAKREPORT to work for this link -* [`SRTO_CONNTIMEO`](../docs/APISocketOptions.md#SRTO_CONNTIMEO): If you want to give more time to connect on this link -* [`SRTO_LOSSMAXTTL`](../docs/APISocketOptions.md#SRTO_LOSSMAXTTL): If this link tends to suffer from UDP reordering -* [`SRTO_PEERIDLETIMEO`](../docs/APISocketOptions.md#SRTO_PEERIDLETIMEO): If you want to be more tolerant for temporary outages -* [`SRTO_GROUPSTABTIMEO`](../docs/APISocketOptions.md#SRTO_GROUPSTABTIMEO): To set ACK jitter tolerance per individual link +* [`SRTO_SNDBUF`](API-socket-options.md#SRTO_SNDBUF): Allows for larger sender buffer for slower links +* [`SRTO_RCVBUF`](API-socket-options.md#SRTO_RCVBUF): Allows for larger receiver buffer for longer recovery +* [`SRTO_UDP_RCVBUF`](API-socket-options.md#SRTO_UDP_RCVBUF): UDP receiver buffer, if this link has a big flight window +* [`SRTO_UDP_SNDBUF`](API-socket-options.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window +* [`SRTO_SNDDROPDELAY`](API-socket-options.md#SRTO_SNDDROPDELAY): When particular link tends to drop too eagerly +* [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT): If you don't want NAKREPORT to work for this link +* [`SRTO_CONNTIMEO`](API-socket-options.md#SRTO_CONNTIMEO): If you want to give more time to connect on this link +* [`SRTO_LOSSMAXTTL`](API-socket-options.md#SRTO_LOSSMAXTTL): If this link tends to suffer from UDP reordering +* [`SRTO_PEERIDLETIMEO`](API-socket-options.md#SRTO_PEERIDLETIMEO): If you want to be more tolerant for temporary outages +* [`SRTO_GROUPSTABTIMEO`](API-socket-options.md#SRTO_GROUPSTABTIMEO): To set ACK jitter tolerance per individual link | Returns | | @@ -1502,9 +1500,12 @@ The following options are allowed to be set on the member socket: [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- + -## Options and properties + +## Options and Properties * [srt_getpeername](#srt_getpeername) * [srt_getsockname](#srt_getsockname) @@ -1512,10 +1513,8 @@ The following options are allowed to be set on the member socket: * [srt_setsockopt, srt_setsockflag](#srt_setsockopt-srt_setsockflag) * [srt_getversion](#srt_getversion) +**NOTE**: For more information, see [Getting and Setting Options](API-socket-options.md#getting-and-setting-options). -**NOTE**: For more information, see [Getting and Setting Options](../docs/APISocketOptions.md#getting-and-setting-options) - - ### srt_getpeername ``` int srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); @@ -1636,7 +1635,6 @@ are then derived by the member sockets. specific option (see option description for details). - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1656,11 +1654,22 @@ readable form, where x = ("%d", (version>>16) & 0xff), etc. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + -## Helper data types for transmission - +## Helper Data Types for Transmission + +* [SRT_MSGCTRL](#SRT_MSGCTRL) + +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. + ### SRT_MSGCTRL The [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure: @@ -1668,15 +1677,15 @@ The [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure: ```c++ typedef struct SRT_MsgCtrl_ { - int flags; // Left for future - int msgttl; // TTL for a message, default -1 (no TTL limitation) - int inorder; // Whether a message is allowed to supersede a partially lost one. Unused in stream and live mode. - int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame - int64_t srctime; // source time (microseconds since SRT internal clock epoch) - int32_t pktseq; // sequence number of the first packet in received message (unused for sending) - int32_t msgno; // message number (output value for both sending and receiving) - SRT_SOCKGROUPDATA* grpdata; // pointer to group data array - size_t grpdata_size; // size of the group array + int flags; // Left for future + int msgttl; // TTL for a message, default -1 (no TTL limitation) + int inorder; // Whether a message is allowed to supersede a partially lost one. Unused in stream and live mode + int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame + int64_t srctime; // Source time, in microseconds since SRT internal clock epoch + int32_t pktseq; // Sequence number of the first packet in received message (unused for sending) + int32_t msgno; // Message number (output value for both sending and receiving) + SRT_SOCKGROUPDATA* grpdata; // Pointer to group data array + size_t grpdata_size; // Size of the group array } SRT_MSGCTRL; ``` @@ -1729,8 +1738,8 @@ call [`srt_sendmsg2`](#srt_sendmsg) or [`srt_recvmsg2`](#srt_recvmsg2) function for a group, you should pass an array here so that you can retrieve the status of particular member sockets. If you pass an array that is too small, your `grpdata_size` field will be rewritten with the current number of members, but without filling in -the array. For details, see the (Bonding introduction)[bonding-intro.md] and -(Socket Groups)[socket-groups.md] documents. +the array. For details, see the (Bonding introduction)[../bonding-intro.md] and +(Socket Groups)[../socket-groups.md] documents. **Helpers for [`SRT_MSGCTRL`](#SRT_MSGCTRL):** @@ -1746,6 +1755,11 @@ pass this constant object into any of the API functions because they require it to be mutable, as they use some fields to output values. +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + ## Transmission @@ -1754,7 +1768,12 @@ to be mutable, as they use some fields to output values. * [srt_recv, srt_recvmsg, srt_recvmsg2](#srt_recv-srt_recvmsg-srt_recvmsg2) * [srt_sendfile, srt_recvfile](#srt_sendfile-srt_recvfile) - +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. + + ### srt_send ### srt_sendmsg ### srt_sendmsg2 @@ -1767,7 +1786,6 @@ int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); Sends a payload to a remote party over a given socket. - **Arguments**: * [`u`](#u): Socket used to send. The socket must be connected for this operation. @@ -1812,14 +1830,12 @@ In both **file/message** and **live mode** the successful return is always equal | [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
**live mode**: trying to send more bytes at once than `SRTO_PAYLOADSIZE` or wrong source time
was provided. | | [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | Incorrect API usage in **stream mode** (reserved for future use):
The congestion controller object used for this mode doesn't use any restrictions on this call,
but this may change. | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Message to be sent can't fit in the sending buffer (that is, it exceeds the current total space in the
sending buffer in bytes). This means that the sender buffer is too small, or the application is
trying to send a larger message than initially predicted. | -| [`SRT_EASYNCSND`](#srt_easyncsnd) | There's no free space currently in the buffer to schedule the payload. This is only reported in
non-blocking mode ([`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) set to false); in blocking mode the call is blocked until
enough free space in the sending buffer becomes available. | -| [`SRT_ETIMEOUT`](#srt_etimeout) | The condition described above still persists and the timeout has passed. This is only reported in
blocking mode when [`SRTO_SNDTIMEO`](../docs/APISocketOptions.md#SRTO_SNDTIMEO) is set to a value other than -1. | +| [`SRT_EASYNCSND`](#srt_easyncsnd) | There's no free space currently in the buffer to schedule the payload. This is only reported in
non-blocking mode ([`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) set to false); in blocking mode the call is blocked until
enough free space in the sending buffer becomes available. | +| [`SRT_ETIMEOUT`](#srt_etimeout) | The condition described above still persists and the timeout has passed. This is only reported in
blocking mode when [`SRTO_SNDTIMEO`](API-socket-options.md#SRTO_SNDTIMEO) is set to a value other than -1. | | [`SRT_EPEERERR`](#srt_epeererr) | This is reported only in the case where, as a stream is being received by a peer, the
[`srt_recvfile`](#srt_recvfile) function encounters an error during a write operation on a file. This is reported by
a `UMSG_PEERERROR` message from the peer, and the agent sets the appropriate flag internally.
This flag persists up to the moment when the connection is broken or closed. | | | | - - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1840,7 +1856,7 @@ Extracts the payload waiting to be received. Note that [`srt_recv`](#srt_recv) a kept for historical reasons. In the UDT predecessor the application was required to use either the `UDT::recv` version for **stream mode** and `UDT::recvmsg` for **message mode**. In SRT this distinction is resolved internally by the -[`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) flag. +[`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag. **Arguments**: @@ -1866,8 +1882,8 @@ the error is reported. 3. In **live mode**, the function behaves as in **file/message mode**, although the number of bytes retrieved will be at most the size of `SRTO_PAYLOADSIZE`. In this mode, -however, with default settings of [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) -and [`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP), the message will be +however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) +and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP), the message will be received only when its time to play has come, and until then it will be kept in the receiver buffer. Also, when the time to play has come for a message that is next to the currently lost one, it will be delivered and the lost one dropped. @@ -1883,15 +1899,14 @@ the currently lost one, it will be delivered and the lost one dropped. |:--------------------------------------------- |:--------------------------------------------------------- | | [`SRT_ENOCONN`](#srt_enoconn) | Socket [`u`](#u) used for the operation is not connected. | | [`SRT_ECONNLOST`](#srt_econnlost) | Socket [`u`](#u) used for the operation has lost connection (this is reported only if the connection
was unexpectedly broken, not when it was closed by the foreign host). | -| [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
-- **live mode**: size of the buffer is less than [`SRTO_PAYLOADSIZE`](../docs/APISocketOptions.md#SRTO_PAYLOADSIZE) | +| [`SRT_EINVALMSGAPI`](#srt_einvalmsgapi) | Incorrect API usage in **message mode**:
-- **live mode**: size of the buffer is less than [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) | | [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | Incorrect API usage in **stream mode**:
• Currently not in use. File congestion control used for **stream mode** does not restrict
the parameters. :warning:   **???** | | [`SRT_ELARGEMSG`](#srt_elargemsg) | Message to be sent can't fit in the sending buffer (that is, it exceeds the current total space in
the sending buffer in bytes). This means that the sender buffer is too small, or the application
is trying to send a larger message than initially intended. | -| [`SRT_EASYNCRCV`](#srt_easyncrcv) | There are no data currently waiting for delivery. This happens only in non-blocking mode
(when [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) is set to false). In blocking mode the call is blocked until the data are ready.
How this is defined, depends on the mode:
• In **live mode** (with [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) on), at least one packet must be present in the receiver
buffer and its time to play be in the past
• In **file/message mode**, one full message must be available, the next one waiting if there are no
messages with `inorder` = false, or possibly the first message ready with `inorder` = false
• In **file/stream mode**, it is expected to have at least one byte of data still not extracted | -| [`SRT_ETIMEOUT`](#srt_etimeout) | The readiness condition described above is still not achieved and the timeout has passed.
This is only reported in blocking mode when[`SRTO_RCVTIMEO`](../docs/APISocketOptions.md#SRTO_RCVTIMEO) is set to a value other than -1. | +| [`SRT_EASYNCRCV`](#srt_easyncrcv) | There are no data currently waiting for delivery. This happens only in non-blocking mode
(when [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to false). In blocking mode the call is blocked until the data are ready.
How this is defined, depends on the mode:
• In **live mode** (with [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) on), at least one packet must be present in the receiver
buffer and its time to play be in the past
• In **file/message mode**, one full message must be available, the next one waiting if there are no
messages with `inorder` = false, or possibly the first message ready with `inorder` = false
• In **file/stream mode**, it is expected to have at least one byte of data still not extracted | +| [`SRT_ETIMEOUT`](#srt_etimeout) | The readiness condition described above is still not achieved and the timeout has passed.
This is only reported in blocking mode when[`SRTO_RCVTIMEO`](API-socket-options.md#SRTO_RCVTIMEO) is set to a value other than -1. | | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -1939,7 +1954,7 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or |:--------------------------------------------- |:----------------------------------------------------------------------------- | | [`SRT_ENOCONN`](#srt_enoconn) | Socket [`u`](#u) used for the operation is not connected. | | [`SRT_ECONNLOST`](#srt_econnlost) | Socket [`u`](#u) used for the operation has lost its connection. | -| [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | When socket has [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) = true or [`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) = true.
(:warning:   **BUG?**: Looxlike MESSAGEAPI isn't checked) | +| [`SRT_EINVALBUFFERAPI`](#srt_einvalbufferapi) | When socket has [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = true or [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = true.
(:warning:   **BUG?**: Looxlike MESSAGEAPI isn't checked) | | [`SRT_EINVRDOFF`](#srt_einvrdoff) | There is a mistake in `offset` or `size` parameters, which should match the index availability
and size of the bytes available since `offset` index. This is actually reported for [`srt_sendfile`](#srt_sendfile)
when the `seekg` or `tellg` operations resulted in error. | | [`SRT_EINVWROFF`](#srt_einvwroff) | Like above, reported for [`srt_recvfile`](#srt_recvfile) and `seekp`/`tellp`. | | [`SRT_ERDPERM`](#srt_erdperm) | The read from file operation has failed ([`srt_sendfile`](#srt_sendfile)). | @@ -1947,14 +1962,18 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +--- + + + +## Performance Tracking -## Performance tracking +* [srt_bstats, srt_bistats](#srt_bstats-srt_bistats) -**Sequence Numbers** +**Sequence Numbers:** The sequence numbers used in SRT are 32-bit "circular numbers" with the most significant bit not included. For example 0x7FFFFFFF shifted forward by 3 becomes 2. As far as any comparison is concerned, it can be thought of as a "distance" which is an integer @@ -1967,9 +1986,6 @@ the required range already, so for a numbers like 0x7FFFFFF0 and 0x10, for which "numeric difference" would be 0x7FFFFFE0, the "distance" is 0x20. -* [srt_bstats, srt_bistats](#srt_bstats-srt_bistats) - - ### srt_bstats ### srt_bistats ``` @@ -1990,11 +2006,17 @@ Reports the current statistics * `instantaneous`: 1 if the statistics should use instant data, not moving averages `SRT_TRACEBSTATS` is an alias to `struct CBytePerfMon`. For a complete description -of the fields please refer to the document [statistics.md](statistics.md). +of the fields please refer to [SRT Statistics](statistics.md). +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) -## Asynchronous operations (epoll) +--- + + + + +## Asynchronous Operations (Epoll) * [srt_epoll_create](#srt_epoll_create) * [srt_epoll_add_usock, srt_epoll_add_ssock, srt_epoll_update_usock, srt_epoll_update_ssock](#srt_epoll_add_usock-srt_epoll_add_ssock-srt_epoll_update_usock-srt_epoll_update_ssock) @@ -2012,8 +2034,8 @@ or writing operation, as it's in blocking mode, it blocks until at least one of the sockets subscribed for a single waiting call in given operation mode is ready to do this operation without blocking. It's usually combined with setting the nonblocking mode on a socket. In SRT this is set separately for reading and -writing ([`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) and -[`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) respectively). This is +writing ([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) and +[`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) respectively). This is to ensure that if there is internal error in the application (or even possibly a bug in SRT that has reported a spurious readiness report) the operation will end up with an error rather than cause blocking, which would be more dangerous for the @@ -2026,9 +2048,6 @@ readiness status of particular operations. The [`srt_epoll_wait`](#srt_epoll_wai function can then be used to block until any readiness status in the whole [`eid`](#eid) is set. - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_epoll_create ``` @@ -2107,7 +2126,7 @@ With [`SRT_EPOLL_ET`](#SRT_EPOLL_ET) flag they become **edge-triggered**. The [`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag is always edge-triggered. It designates a special event that happens on a group, or on a listener socket that -has the [`SRTO_GROUPCONNECT`](../docs/APISocketOptions.md#SRTO_GROUPCONNECT) flag +has the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag set to allow group connections. This flag is triggered in the following situations: * for group connections, when a new link has been established for a group that @@ -2251,7 +2270,6 @@ the only way to know what kind of error has occurred on the socket. | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2338,7 +2356,6 @@ container identified by [`eid`](#eid). | | | - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2373,8 +2390,8 @@ the general output array is not empty. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------- | -| | This function returns the state of the flags at the time before the call. | -| -1 | Special value in case when an error occurred. | +| | This function returns the state of the flags at the time before the call | +| -1 | Special value in case when an error occurred | | | | | Errors | | @@ -2396,8 +2413,8 @@ Deletes the epoll container. | Returns | | |:----------------------------- |:-------------------------------------------------------------- | -| | The number (\>0) of ready sockets, of whatever kind (if any). | -| -1 | Error . | +| | The number (\>0) of ready sockets, of whatever kind (if any) | +| -1 | Error | | | | | Errors | | @@ -2406,9 +2423,14 @@ Deletes the epoll container. | | | +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + + -## Logging control +## Logging Control * [srt_setloglevel](#srt_setloglevel) * [srt_addlogfa, srt_dellogfa, srt_resetlogfa](#srt_addlogfa-srt_dellogfa-srt_resetlogfa) @@ -2424,10 +2446,6 @@ entries up to the *Note* log level are displayed and from all FAs. Logging can only be manipulated globally, with no regard to a specific socket. This is because lots of operations in SRT are not dedicated to any particular socket, and some are shared between sockets. - - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_setloglevel @@ -2513,6 +2531,7 @@ The following flags are available, as collected in the `logging_api.h` public he - `SRT_LOGF_DISABLE_SEVERITY`: Do not provide severity information in the header - `SRT_LOGF_DISABLE_EOL`: Do not add the end-of-line character to the log line + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2579,10 +2598,6 @@ The clock used by the SRT internal clock is determined by the following build fl The default is currently to use the system clock as the internal SRT clock, although it's highly recommended to use one of the above monotonic clocks, as system clock is vulnerable to time modifications during transmission. - - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - ### srt_time_now @@ -2612,11 +2627,11 @@ Get connection time in microseconds elapsed since epoch using SRT internal clock (steady or monotonic clock). The connection time represents the time when SRT socket was open to establish a connection. Milliseconds elapsed since connection start time can be determined using [**Performance tracking**](#Performance-tracking) functions -and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [statistics.md](statistics.md)). +and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistics.md)). | Returns | | |:----------------------------- |:--------------------------------------------------------------------------- | -| | Connection time in microseconds elapsed since epoch of SRT internal clock. | +| | Connection time in microseconds elapsed since epoch of SRT internal clock | | -1 | Error | | | | @@ -2630,17 +2645,10 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [statistics.md](statistics --- -## Diagnostics -General notes concerning the `getlasterror` diagnostic functions: when an API -function ends up with error, this error information is stored in a thread-local -storage. This means that you'll get the error of the operation that was last -performed as long as you call this diagnostic function just after the failed -function has returned. In any other situation the information provided by the -diagnostic function is undefined. -**NOTE**: There is a list of [Error Codes](#error-codes) at the bottom of this document. +## Diagnostics * [srt_getlasterror_str](#srt_getlasterror_str) * [srt_getlasterror](#srt_getlasterror) @@ -2650,7 +2658,16 @@ diagnostic function is undefined. * [srt_rejectreason_str](#srt_rejectreason_str) * [srt_setrejectreason](#srt_setrejectreason) - +General notes concerning the `getlasterror` diagnostic functions: when an API +function ends up with error, this error information is stored in a thread-local +storage. This means that you'll get the error of the operation that was last +performed as long as you call this diagnostic function just after the failed +function has returned. In any other situation the information provided by the +diagnostic function is undefined. + +**NOTE**: There are lists of rejection reasons and error codes at the bottom of this section. + + ### srt_getlasterror ``` @@ -2683,7 +2700,6 @@ as long as only one thread in the whole application calls this function at the moment* - [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2714,7 +2730,6 @@ report a "successful" code. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - --- ### srt_rejectreason_str @@ -2751,7 +2766,7 @@ back to the caller peer with the handshake response. Note that allowed values for this function begin with `SRT_REJC_PREDEFINED` (that is, you cannot set a system rejection code). For example, your application can inform the calling side that the resource specified under the `r` key in the -StreamID string (see [`SRTO_STREAMID`](../docs/APISocketOptions.md#SRTO_STREAMID)) +StreamID string (see [`SRTO_STREAMID`](API-socket-options.md#SRTO_STREAMID)) is not available - it then sets the value to `SRT_REJC_PREDEFINED + 404`. | Returns | | @@ -2766,8 +2781,8 @@ is not available - it then sets the value to `SRT_REJC_PREDEFINED + 404`. | [`SRT_EINVPARAM`](#srt_einvparam) | `value` is less than `SRT_REJC_PREDEFINED` | | | | -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -2779,16 +2794,20 @@ int srt_getrejectreason(SRTSOCKET sock); This function provides a more detailed reason for a failed connection attempt. It shall be called after a connecting function (such as [`srt_connect`](#srt_connect)) has returned an error, the code for which is [`SRT_ECONNREJ`](#srt_econnrej). If -[`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) has been set on the socket +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) has been set on the socket used for the connection, the function should also be called when the [`SRT_EPOLL_ERR`](#SRT_EPOLL_ERR) event is set for this socket. It returns a numeric code, which can be translated into a message by [`srt_rejectreason_str`](#srt_rejectreason_str). - -## Rejection Reasons - +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### Rejection Reasons + + #### SRT_REJ_UNKNOWN A fallback value for cases when there was no connection rejected. @@ -2817,8 +2836,6 @@ The data sent by one party to another cannot be properly interpreted. This should not happen during normal usage, unless it's a bug, or some weird events are happening on the network. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_BACKLOG @@ -2857,8 +2874,6 @@ the sent handshake packets are returning to the same host as if they were sent by the peer (i.e. a party is sending to itself). When this happens, this reject reason will be reported by every attempt. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_BADSECRET @@ -2868,28 +2883,26 @@ Both parties have defined a passphrase for connection, but they differ. #### SRT_REJ_UNSECURE Only one connection party has set up a password. See also the -[`SRTO_ENFORCEDENCRYPTION`](../docs/APISocketOptions.md#SRTO_ENFORCEDENCRYPTION) flag. +[`SRTO_ENFORCEDENCRYPTION`](API-socket-options.md#SRTO_ENFORCEDENCRYPTION) flag. #### SRT_REJ_MESSAGEAPI -The value of the [`SRTO_MESSAGEAPI`](../docs/APISocketOptions.md#SRTO_MESSAGEAPI) +The value of the [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties. #### SRT_REJ_CONGESTION -The [`SRTO_CONGESTION`](../docs/APISocketOptions.md#SRTO_CONGESTION)option has +The [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION)option has been set up differently on both connection parties. #### SRT_REJ_FILTER -The [`SRTO_PACKETFILTER`](../docs/APISocketOptions.md#SRTO_PACKETFILTER) option +The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - #### SRT_REJ_GROUP @@ -2905,7 +2918,7 @@ completely different from the existing connections in the bonding group. The connection wasn't rejected, but it timed out. This code is always set on connection timeout, but this is the only way to get this state in non-blocking -mode (see [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN)). +mode (see [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN)). There may also be server and user rejection codes, as defined by the `SRT_REJC_INTERNAL`, `SRT_REJC_PREDEFINED` and `SRT_REJC_USERDEFINED` @@ -2917,9 +2930,9 @@ the application. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - - -## Error Codes +--- + +### Error Codes All functions that return the status via `int` value return -1 (designated as `SRT_ERROR`) always when the call has failed (in case of resource creation @@ -2929,50 +2942,48 @@ functions an appropriate symbol is defined, like `SRT_INVALID_SOCK` for `SRT_ERRNO` enum: -#### `SRT_EUNKNOWN` +#### SRT_EUNKNOWN Internal error when setting the right error code. -#### `SRT_SUCCESS` +#### SRT_SUCCESS The value set when the last error was cleared and no error has occurred since then. -#### `SRT_ECONNSETUP` +#### SRT_ECONNSETUP General setup error resulting from internal system state. -#### `SRT_ENOSERVER` +#### SRT_ENOSERVER Connection timed out while attempting to connect to the remote address. Note that when this happens, [`srt_getrejectreason`](#srt_getrejectreason) also reports the timeout reason. -#### `SRT_ECONNREJ` +#### SRT_ECONNREJ Connection has been rejected. Additional reject reason can be obtained through [`srt_getrejectreason`](#srt_getrejectreason) (see above). -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_ESOCKFAIL` +#### SRT_ESOCKFAIL An error occurred when trying to call a system function on an internally used UDP socket. Note that the detailed system error is available in the extra variable passed by pointer to `srt_getlasterror`. -#### `SRT_ESECFAIL` +#### SRT_ESECFAIL A possible tampering with the handshake packets was detected, or an encryption request wasn't properly fulfilled. -#### `SRT_ESCLOSED` +#### SRT_ESCLOSED A socket that was vital for an operation called in blocking mode has been closed during the operation. Please note that this situation is @@ -2984,85 +2995,79 @@ parameter to [`srt_connect*`](#srt_connect) or [`srt_accept`](#srt_accept) is no longer usable. -#### `SRT_ECONNFAIL` +#### SRT_ECONNFAIL General connection failure of unknown details. -#### `SRT_ECONNLOST` +#### SRT_ECONNLOST The socket was properly connected, but the connection has been broken. This specialization is reported from the transmission functions. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_ENOCONN` +#### SRT_ENOCONN The socket is not connected. This can be reported also when the connection was broken for a function that checks some characteristic socket data. -#### `SRT_ERESOURCE` +#### SRT_ERESOURCE System or standard library error reported unexpectedly for unknown purpose. Usually it means some internal error. -#### `SRT_ETHREAD` +#### SRT_ETHREAD System was unable to spawn a new thread when required. -#### `SRT_ENOBUF` +#### SRT_ENOBUF System was unable to allocate memory for buffers. -#### `SRT_ESYSOBJ` +#### SRT_ESYSOBJ System was unable to allocate system specific objects (such as sockets, mutexes or condition variables). -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EFILE` +#### SRT_EFILE General filesystem error (for functions operating with file transmission). -#### `SRT_EINVRDOFF` +#### SRT_EINVRDOFF Failure when trying to read from a given position in the file (file could be modified while it was read from). -#### `SRT_ERDPERM` +#### SRT_ERDPERM Read permission was denied when trying to read from file. -#### `SRT_EINVWROFF` +#### SRT_EINVWROFF Failed to set position in the written file. -#### `SRT_EWRPERM` +#### SRT_EWRPERM Write permission was denied when trying to write to a file. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EINVOP` +#### SRT_EINVOP Invalid operation performed for the current state of a socket. This mainly -concerns performing `srt_bind*` operations on a socket that is already bound. +concerns performing `srt_bind*` operations on a socket that is already bound. Once a socket has been been bound, it cannot be bound again. -#### `SRT_EBOUNDSOCK` +#### SRT_EBOUNDSOCK The socket is currently bound and the required operation cannot be performed in this state. Usually it's about an option that can only @@ -3070,7 +3075,7 @@ be set on the socket before binding (`srt_bind*`). Note that a socket that is currently connected is also considered bound. -#### `SRT_ECONNSOCK` +#### SRT_ECONNSOCK The socket is currently connected and therefore performing the required operation is not possible. Usually concerns setting an option that must be set before @@ -3080,7 +3085,7 @@ isn't in a state that allows it (only [`SRTS_INIT`](#SRTS_INIT) or [`SRTS_OPENED`](#SRTS_OPENED) are allowed). -#### `SRT_EINVPARAM` +#### SRT_EINVPARAM This error is reported in a variety of situations when call parameters for API functions have some requirements defined and these were not @@ -3089,17 +3094,15 @@ parameters of the call before even performing any operation. This error can be easily avoided if you set the values correctly. -#### `SRT_EINVSOCK` +#### SRT_EINVSOCK The API function required an ID of an entity (socket or group) and it was invalid. Note that some API functions work only with socket or only with group, so they would also return this error if inappropriate type of entity was passed, even if it was valid. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EUNBOUNDSOCK` +#### SRT_EUNBOUNDSOCK The operation to be performed on a socket requires that it first be explicitly bound (using [`srt_bind*`](#srt_bind) functions). Currently it applies when @@ -3107,23 +3110,23 @@ calling [`srt_listen`](#srt_listen), which cannot work with an implicitly bound socket. -#### `SRT_ENOLISTEN` +#### SRT_ENOLISTEN The socket passed for the operation is required to be in the listen state ([`srt_listen`](#srt_listen) must be called first). -#### `SRT_ERDVNOSERV` +#### SRT_ERDVNOSERV The required operation cannot be performed when the socket is set to rendezvous -mode ([`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) set to true). +mode ([`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) set to true). Usually applies when trying to call [`srt_listen`](#srt_listen) on such a socket. -#### `SRT_ERDVUNBOUND` +#### SRT_ERDVUNBOUND An attempt was made to connect to a socket set to rendezvous mode -([`SRTO_RENDEZVOUS`](../docs/APISocketOptions.md#SRTO_RENDEZVOUS) set to true) +([`SRTO_RENDEZVOUS`](API-socket-options.md#SRTO_RENDEZVOUS) set to true) that was not first bound. A rendezvous connection requires setting up two addresses and ports on both sides of the connection, then setting the local one with [`srt_bind`](#srt_bind) and using the remote one with [`srt_connect`](#srt_connect) @@ -3133,42 +3136,40 @@ state) that is to be bound implicitly is only allowed for regular caller sockets (not rendezvous). -#### `SRT_EINVALMSGAPI` +#### SRT_EINVALMSGAPI The function was used incorrectly in the message API. This can happen if: * The parameters specific for the message API in [`SRT_MSGCTRL`](#SRT_MSGCTRL) -type parameter were incorrectly specified +type parameter were incorrectly specified. -* The extra parameter check performed by the congestion controller has failed +* The extra parameter check performed by the congestion controller has failed. * The socket is a member of a self-managing group, therefore you should -perform the operation on the group, not on this socket - -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) +perform the operation on the group, not on this socket. -#### `SRT_EINVALBUFFERAPI` +#### SRT_EINVALBUFFERAPI The function was used incorrectly in the stream (buffer) API, that is, either the stream-only functions were used with set message API ([`srt_sendfile`](#srt_sendfile)/[`srt_recvfile`](#srt_recvfile)) -or TSBPD mode was used with buffer API ([`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) set to true) +or TSBPD mode was used with buffer API ([`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) set to true) or the congestion controller has failed to check call parameters. -#### `SRT_EDUPLISTEN` +#### SRT_EDUPLISTEN The port tried to be bound for listening is already busy. Note that binding to the same port -is allowed in general (when [`SRTO_REUSEADDR`](../docs/APISocketOptions.md#SRTO_REUSEADDRS) +is allowed in general (when [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is true on every socket that has bound it), but only one such socket can be a listener. -#### `SRT_ELARGEMSG` +#### SRT_ELARGEMSG Size exceeded. This is reported in the following situations: * Trying to receive a message, but the read-ready message is larger than -the buffer passed to the receiving function +the buffer passed to the receiving function. * Trying to send a message, but the size of this message exceeds the size of the preset sender buffer, so it cannot be stored in the sender buffer. @@ -3176,12 +3177,12 @@ size of the preset sender buffer, so it cannot be stored in the sender buffer. * When getting group data, the array to be filled is too small. -#### `SRT_EINVPOLLID` +#### SRT_EINVPOLLID -The epoll ID passed to an epoll function is invalid +The epoll ID passed to an epoll function is invalid. -#### `SRT_EPOLLEMPTY` +#### SRT_EPOLLEMPTY The epoll container currently has no subscribed sockets. This is reported by an epoll waiting function that would in this case block forever. This problem @@ -3193,50 +3194,48 @@ by setting the `SRT_EPOLL_ENABLE_EMPTY` flag, which may be useful when you use multiple threads and start waiting without subscribed sockets, so that you can subscribe them later from another thread. -[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) - -#### `SRT_EASYNCFAIL` +#### SRT_EASYNCFAIL General asynchronous failure (not in use currently). -#### `SRT_EASYNCSND` +#### SRT_EASYNCSND Sending operation is not ready to perform. This error is reported when trying to perform a sending operation on a socket that is not ready for sending, but -[`SRTO_SNDSYN`](../docs/APISocketOptions.md#SRTO_SNDSYN) was set to false (when +[`SRTO_SNDSYN`](API-socket-options.md#SRTO_SNDSYN) was set to false (when true, the function would block the call otherwise). -#### `SRT_EASYNCRCV` +#### SRT_EASYNCRCV Receiving operation is not ready to perform. This error is reported when trying to perform a receiving operation or accept a new socket from the listener socket, when -the socket is not ready for that operation, but [`SRTO_RCVSYN`](../docs/APISocketOptions.md#SRTO_RCVSYN) +the socket is not ready for that operation, but [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) was set to false (when true, the function would block the call otherwise). -#### `SRT_ETIMEOUT` +#### SRT_ETIMEOUT The operation timed out. This can happen if you have a timeout set by an option -([`SRTO_RCVTIMEO`](../docs/APISocketOptions.md#SRTO_RCVTIMEO) or -[`SRTO_SNDTIMEO`](../docs/APISocketOptions.md#SRTO_SNDTIMEO)), or passed as an +([`SRTO_RCVTIMEO`](API-socket-options.md#SRTO_RCVTIMEO) or +[`SRTO_SNDTIMEO`](API-socket-options.md#SRTO_SNDTIMEO)), or passed as an extra argument ([`srt_epoll_wait`](#srt_epoll_wait) or [`srt_accept_bond`](#srt_accept_bond)) and the function call was blocking, but the required timeout time has passed. -#### `SRT_ECONGEST` +#### SRT_ECONGEST **NOTE**: This error is used only in an experimental version that requires setting the `SRT_ENABLE_ECN` macro at compile time. Otherwise the situation described below results in the usual successful report. This error should be reported by the sending function when, with -[`SRTO_TSBPDMODE`](../docs/APISocketOptions.md#SRTO_TSBPDMODE) and -[`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) set to true, some +[`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and +[`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) set to true, some packets were dropped at the sender side (see the description of -[`SRTO_TLPKTDROP`](../docs/APISocketOptions.md#SRTO_TLPKTDROP) for details). This +[`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) for details). This doesn't concern the data that were passed for sending by the sending function (these data are placed at the back of the sender buffer, while the dropped packets are at the front). In other words, the operation done by the sending @@ -3244,7 +3243,7 @@ function is successful, but the application might want to slow down the sending rate to avoid congestion. -#### `SRT_EPEERERR` +#### SRT_EPEERERR This error is reported when a receiver peer is writing to a file that an agent is sending. When the peer encounters an error when writing the received data to @@ -3252,7 +3251,4 @@ a file, it sends the `UMSG_PEERERROR` message back to the sender, and the sender reports this error from the API sending function. - - - -[RETURN TO TOP OF PAGE](#SRT-API-Functions) +[Return to Top of Page](#SRT-API-Functions) diff --git a/docs/APISocketOptions.md b/docs/API/API-socket-options.md similarity index 98% rename from docs/APISocketOptions.md rename to docs/API/API-socket-options.md index 149241a73..2dfc3bd8f 100644 --- a/docs/APISocketOptions.md +++ b/docs/API/API-socket-options.md @@ -1,18 +1,13 @@ -# SRT Socket Options +# SRT API Socket Options There is a general method of setting options on a socket in the SRT C API, similar to the system `setsockopt/getsockopt` functions. -**NOTE**: This document replaces the socket option description originally -in [api.md](https://github.com/Haivision/srt/blob/master/docs/API.md) +- [Types Used in Socket Options](#types-used-in-socket-options) +- [Getting and Setting Options](#getting-and-setting-options) +- [List of Options](#list-of-options) -**Sections:** - -- [Types used in socket options](#types-used-in-socket-options) -- [Getting and setting options](#getting-and-setting-options) -- [List of options](#list-of-options) - -## Types used in socket options +## Types Used in Socket Options Possible types of socket options are: @@ -20,7 +15,7 @@ Possible types of socket options are: does not change size on 64-bit systems. For clarity, options use this fixed size integer. In some cases the value is expressed using an enumeration type (see below). -- `int64_t` - Some options need the parameter specified as 64-bit integer +- `int64_t` - Some options need the parameter specified as 64-bit integer. - `bool` - Requires the use of a boolean type (`` for C, or built-in for C++). When *setting* an option, passing the value through an `int` type is @@ -36,7 +31,7 @@ read should specify the maximum length of that array. - `linger` - Linger structure. Used exclusively with `SRTO_LINGER`. -### Enumeration types used in options +### Enumeration Types Used in Options #### `SRT_TRANSTYPE` @@ -45,7 +40,7 @@ Used by `SRTO_TRANSTYPE` option: - `SRTT_LIVE`: Live mode. - `SRTT_FILE`: File mode. -See [Transmission types](./API.md#transmission-types) for details. +See [Transmission Types](API.md#transmission-types) for details. #### `SRT_KM_STATE` @@ -85,7 +80,7 @@ one party has set a password, in which case the KM state is as follows: | Party with no password: | `SRT_KM_S_NOSECRET` | `SRT_KM_S_UNSECURED` | | Party with password: | `SRT_KM_S_UNSECURED` | `SRT_KM_S_NOSECRET` | -## Getting and setting options +## Getting and Setting Options Legacy version: @@ -194,9 +189,9 @@ The + marker can only coexist with GS. Possible specifications are: `srt_create_config`. Note that this setting may override the setting derived from the group. -## List of options +## List of Options -The following table lists SRT socket options in alphabetical order. Option details are given further below. +The following table lists SRT API socket options in alphabetical order. Option details are given further below. | Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | | :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| @@ -775,7 +770,7 @@ complete (not all packets received or there was a packet loss) it will not be copied to the application's buffer. Messages that are sent later, but were earlier reassembled by the receiver, will be delivered once ready, if the `inorder` flag was set to false. -See [`srt_sendmsg`](https://github.com/Haivision/srt/blob/master/docs/API.md#sending-and-receiving)). +See [`srt_sendmsg`](API.md#sending-and-receiving)). As a comparison to the standard system protocols, the Stream API does transmission similar to TCP, whereas the Message API functions like the @@ -1432,7 +1427,7 @@ will be able to retrieve this stream ID from the socket that is returned from `srt_accept` (for a connected socket with that stream ID). You usually use SET on the socket used for `srt_connect`, and GET on the socket retrieved from `srt_accept`. This string can be used completely free-form. However, it's highly -recommended to follow the [SRT Access Control guidlines](AccessControl.md). +recommended to follow the [SRT Access Control guidlines](../AccessControl.md). - As this uses internally the `std::string` type, there are additional functions for it in the legacy/C++ API (udt.h): `srt::setstreamid` and `srt::getstreamid`. diff --git a/docs/API.md b/docs/API/API.md similarity index 93% rename from docs/API.md rename to docs/API/API.md index a124dac03..f1d3a1209 100644 --- a/docs/API.md +++ b/docs/API/API.md @@ -12,40 +12,25 @@ in `transmitmedia.*` files in the `apps` directory which is used by all applications. See `SrtSource::Read` and `SrtTarget::Write` as examples of how data are read and written in SRT. -- [Setup and teardown](#setup-and-teardown) -- [Creating and destroying a socket](#creating-and-destroying-a-socket) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Important Remarks](#important-remarks) -- [Binding and connecting](#binding-and-connecting) - - [Synopsis](#synopsis) - - [SRT Usage - listener (server)](#srt-usage---listener-server) - - [SRT Usage - rendezvous](#srt-usage---rendezvous) +- [Setup and Teardown](#setup-and-teardown) +- [Creating and Destroying a Socket](#creating-and-destroying-a-socket) +- [Binding and Connecting](#binding-and-connecting) - [Sending and Receiving](#sending-and-receiving) - - [Synopsis](#synopsis) - - [Usage](#usage) - - [Transmission types available in SRT](#transmission-types-available-in-srt) -- [Blocking and Non-blocking Mode](#blocking-and-non-blocking-mode) -- [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events)) - - [Synopsis](#synopsis) - - [SRT Usage](#srt-usage) - - [Transmission types](#transmission-types) - - [Terminology](#terminology) - - [Transmission method: Live](#transmission-method-live) - - [Transmission method: Buffer](#transmission-method-buffer) - - [Transmission method: Message](#transmission-method-message) - -**NOTE**: The socket option descriptions originally contained in this document -have been moved to [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md). - -## Setup and teardown +- [Blocking and Non-blocking Modes](#blocking-and-non-blocking-mode) + - [EPoll (Non-blocking Mode Events)](#epoll-non-blocking-mode-events) +- [Transmission Types](#transmission-types) + - [Transmission Method: Live](#transmission-method-live) + - [Transmission Method: Buffer](#transmission-method-buffer) + - [Transmission Method: Message](#transmission-method-message) + +## Setup and Teardown Before any part of the SRT C API can be used, the user should call the `srt_startup()` function. Likewise, before the application exits, the `srt_cleanup()` function should be called. Note that one of the things the startup function does is to create a new thread, so choose the point of execution for these functions carefully. -## Creating and destroying a socket +## Creating and Destroying a Socket To do anything with SRT, you first have to create an SRT socket. The term "socket" in this case is used because of its logical similarity to system-wide sockets. @@ -92,7 +77,7 @@ port". However SRT offers more flexibility than UDP (or TCP, the more logical similarity) because it manages ports as its own resources. For example, one port may be shared between various services. -## Binding and connecting +## Binding and Connecting Connections are established using the same philosophy as TCP, using functions with names and signatures similar to the BSD Socket API. What is new here is @@ -253,7 +238,7 @@ forwarding SRT streams. It permits pulling and pushing of the sender's original time stamp, converted to local time and drift adjusted. The `srctime` parameter is the number of usec (since epoch) in local SRT clock time. If the connection is not between SRT peers or if **Timestamp-Based Packet Delivery mode (TSBPDMODE)** -is not enabled (see [APISocketOptions.md](https://github.com/Haivision/srt/blob/master/docs/APISocketOptions.md)), +is not enabled (see [SRT API Socket Options](API-socket-options.md)), the extracted `srctime` will be 0. Passing `srctime = 0` in `sendmsg` is like using the API without `srctime` and the local send time will be used (if TSBPDMODE is enabled and receiver supports it). @@ -293,10 +278,10 @@ SRT_MSGCTRL mc = srt_msgctrl_default; nb = srt_recvmsg2(u, buf, nb, &mc); ``` -### Transmission types available in SRT +### Transmission Types Available in SRT Mode settings determine how the sender and receiver functions work. The main -[socket options](APISocketOptions.md) that control it are: +[socket options](API-socket-options.md) that control it are: - `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected mode: @@ -308,9 +293,9 @@ mode: See [Transmission types](#transmission-types) below. -## Blocking and Non-blocking Mode +## Blocking and Non-blocking Modes -SRT functions can also work in blocking and non-blocking mode, for which +SRT functions can also work in blocking and non-blocking modes, for which there are two separate options for sending and receiving: `SRTO_SNDSYN` and `SRTO_RCVSYN`. When blocking mode is used, a function will not exit until the availability condition is satisfied. In non-blocking mode the function @@ -323,13 +308,13 @@ and receiving. For example, `SNDSYN` defines blocking for `srt_connect` and `RCVSYN` defines blocking for `srt_accept`. The `SNDSYN` also makes `srt_close` exit only after the sending buffer is completely empty. -## EPoll (Non-blocking Mode Events) +### EPoll (Non-blocking Mode Events) EPoll is a mechanism to track the events happening on the sockets, both "system sockets" (see `SYSSOCKET` type) and SRT Sockets. Note that `SYSSOCKET` is also an alias for `int`, used only for clarity. -### Synopsis +#### Synopsis ```c++ int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL); @@ -341,7 +326,7 @@ int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTim int srt_epoll_clear_usocks(int eid); ``` -### SRT Usage +#### Usage SRT socket being a user level concept, the system epoll (or other select) cannot be used to handle SRT non-blocking mode events. Instead, SRT provides a @@ -413,7 +398,7 @@ when system sockets are involved, is also 10ms. The return time from a poll function can only be quicker when there is an event raised on one of the active SRT sockets. -### `srt_epoll_uwait` +#### `srt_epoll_uwait` In this function only the SRT sockets can be subscribed (it reports error if you pass an epoll id that is subscribed to system sockets). @@ -440,7 +425,12 @@ the epoll container. The SRT EPoll system does not supports all features of Linux epoll. For example, it only supports level-triggered events for system sockets. -### Transmission types +## Transmission Types + +**NOTE:** There might be a difference in terminology used in [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and current documentation. +Please consult [Data Transmission Modes](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.2) +and [Best Practices and Configuration Tips for Data Transmission via SRT](https://tools.ietf.org/html/draft-sharabayko-srt-00#page-71) +sections of the RFC additionally. The current section is going to be reworked accordingly. SRT was originally intended to be used for Live Streaming and therefore its main and default transmission type is "live". However, SRT supports the modes that @@ -527,9 +517,9 @@ lost, or at least not for all still unacknowledged packets. The congestion contr class is responsible for the algorithm for taking care of this situation, which is either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. -### Transmission method: Live +### Transmission Method: Live -Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](APISocketOptions.md): +Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](API-socket-options.md): - `SRTO_TSBPDMODE` = true - `SRTO_RCVLATENCY` = 120 @@ -563,7 +553,7 @@ Otherwise the behavior is undefined and might be surprisingly disappointing. The reading function will always return only a payload that was sent, and it will HANGUP until the time to play has come for this packet (if TSBPD mode is on) or when it is available without gaps of -lost packets (if TSBPD mode is off - see [`SRTO_TSBPDMODE`](APISocketOptions.md#SRTO_TSBPDMODE)). +lost packets (if TSBPD mode is off - see [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE)). You may wish to tweak some of the parameters below: @@ -604,9 +594,9 @@ too long before acknowledging them. This mechanism isn't used (i.e. the BLIND RE situation isn't handled at all) when `SRTO_NAKREPORT` is set by the peer -- the NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. -### Transmission method: Buffer +### Transmission Method: Buffer -Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](APISocketOptions.md): +Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](API-socket-options.md): - `SRTO_TSBPDMODE` = false - `SRTO_RCVLATENCY` = 0 @@ -652,7 +642,7 @@ designate features used in Live mode. None are used with File mode. The only opt that makes sense to modify after the `SRTT_FILE` type was set is `SRTO_MESSAGEAPI`, which is described below. -### Transmission method: Message +### Transmission Method: Message Setting `SRTO_TRANSTYPE` to `SRTT_FILE` and then setting `SRTO_MESSAGEAPI` to `true` implies usage of the Message transmission method. Parameters are set as @@ -708,7 +698,6 @@ Note that you can use any of the sending and receiving functions for sending and receiving messages, except `sendfile/recvfile`, which are dedicated exclusively for Buffer API. -For more information, see [APISocketOptions.md](APISocketOptions.md). +For more information, see [SRT API Socket Options](API-socket-options.md). -[Return to top](#srt-api) - +[Return to Top of Page](#srt-api) diff --git a/docs/statistics.md b/docs/API/statistics.md similarity index 93% rename from docs/statistics.md rename to docs/API/statistics.md index 62a37bcbf..f82b8074c 100644 --- a/docs/statistics.md +++ b/docs/API/statistics.md @@ -23,7 +23,7 @@ The following API functions can be used to retrieve statistics on an SRT socket: * `int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear)` * `int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous)` -Refer to the documentation of the [API functions](API-functions.md) for usage instructions. +Refer to the documentation of the [SRT API Functions](API-functions.md) for usage instructions. ### Summary Table @@ -134,19 +134,19 @@ The time elapsed, in milliseconds, since the SRT socket has been created (after The total number of sent DATA packets, including retransmitted packets ([pktRetransTotal](#pktRetransTotal)). Available for sender. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic counts sent packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) as well. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic counts sent packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) as well. Introduced in SRT v1.4.0. #### pktRecvTotal The total number of received DATA packets, including retransmitted packets ([pktRcvRetransTotal](#pktRcvRetransTotal)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic counts received packet filter control packets ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)) as well. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic counts received packet filter control packets ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)) as well. Introduced in SRT v1.4.0. #### pktSentUniqueTotal The total number of *unique* DATA packets sent by the SRT sender. Available for sender. -This value contains only *unique* *original* DATA packets. Retransmitted DATA packets ([pktRetransTotal](#pktRetransTotal)) are not taken into account. If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](https://cac-word-edit.officeapps.live.com/we/API.md)), packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) are also not taken into account. +This value contains only *unique* *original* DATA packets. Retransmitted DATA packets ([pktRetransTotal](#pktRetransTotal)) are not taken into account. If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), packet filter control packets ([pktSndFilterExtraTotal](#pktSndFilterExtraTotal)) are also not taken into account. This value corresponds to the number of original DATA packets sent by the SRT sender. It counts every packet sent over the network for the first time, and can be calculated as follows: `pktSentUniqueTotal = pktSentTotal – pktRetransTotal`, or by `pktSentUniqueTotal = pktSentTotal – pktRetransTotal - pktSndFilterExtraTotal` if the `SRTO_PACKETFILTER` socket option is enabled. The original DATA packets are sent only once. @@ -158,11 +158,11 @@ Unique means "first arrived" DATA packets. There is no difference whether a pack This statistic doesn't count -- duplicate packets (retransmitted or sent several times by defective hardware/software), +- duplicate packets (retransmitted or sent several times by defective hardware/software), - arrived too late packets (retransmitted or original packets arrived out of order) that were already dropped by the TLPKTDROP mechanism (see [pktRcvDropTotal](#pktRcvDropTotal) statistic), - arrived in time packets, but decrypted with errors (see [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic), and, as a result, dropped by the TLPKTDROP mechanism (see [pktRcvDropTotal](#pktRcvDropTotal) statistic). -DATA packets recovered by the packet filter ([pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal)) are taken into account if the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)). Do not mix up with the control packets received by the packet filter ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)). +DATA packets recovered by the packet filter ([pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal)) are taken into account if the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)). Do not mix up with the control packets received by the packet filter ([pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal)). #### pktSndLossTotal @@ -224,11 +224,11 @@ The total accumulated time in microseconds, during which the SRT sender has some The total number of _dropped_ by the SRT sender DATA packets that have no chance to be delivered in time (refer to [TLPKTDROP](https://github.com/Haivision/srt-rfc/blob/master/draft-sharabayko-mops-srt.md#too-late-packet-drop-too-late-packet-drop) mechanism). Available for sender. -Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [API.md](API.md). +Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [SRT API Socket Options](API-socket-options.md). The delay before TLPKTDROP mechanism is triggered is calculated as follows `SRTO_PEERLATENCY + SRTO_SNDDROPDELAY + 2 * interval between sending ACKs`, -where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds an extra to `SRTO_PEERLATENCY` delay, the default `interval between sending ACKs` is 10 milliseconds. The minimum delay is `1000 + 2 * interval between sending ACKs` milliseconds. Refer to `SRTO_PEERLATENCY`, `SRTO_SNDDROPDELAY` socket options in [API.md](API.md). +where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds an extra to `SRTO_PEERLATENCY` delay, the default `interval between sending ACKs` is 10 milliseconds. The minimum delay is `1000 + 2 * interval between sending ACKs` milliseconds. Refer to `SRTO_PEERLATENCY`, `SRTO_SNDDROPDELAY` socket options in [SRT API Socket Options](API-socket-options.md). #### pktRcvDropTotal @@ -238,7 +238,7 @@ This statistic counts - arrived too late packets (retransmitted or original packets arrived out of order), - arrived in time packets, but decrypted with errors (see also [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic). -Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [API.md](API.md). +Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTDROP` socket options are enabled, refer to [SRT API Socket Options](API-socket-options.md). #### pktRcvUndecryptTotal @@ -246,31 +246,31 @@ The total number of packets that failed to be decrypted at the receiver side. Av #### pktSndFilterExtraTotal -The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for sender. +The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for sender. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterExtraTotal -The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterSupplyTotal -The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterLossTotal -The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. -If the `SRTO_PACKETFILTER` socket option is disabled (refer to [API.md](API.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. +If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### byteSentTotal @@ -363,25 +363,25 @@ Same as [pktRecvNAKTotal](#pktRecvNAKTotal), but for a specified interval. Same as [pktSndFilterExtraTotal](#pktSndFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterExtra Same as [pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterSupply Same as [pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### pktRcvFilterLoss Same as [pktRcvFilterLossTotal](#pktRcvFilterLossTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). #### mbpsSendRate @@ -592,7 +592,7 @@ conditions a nonzero value might be be provided by a congestion control module, although none of the built-in congestion control modules currently use it. -Refer to `SRTO_MAXBW` and `SRTO_INPUTBW` in [API.md](API.md). +Refer to `SRTO_MAXBW` and `SRTO_INPUTBW` in [SRT API Socket Options](API-socket-options.md). #### byteMSS @@ -602,7 +602,7 @@ Should not exceed the size of the maximum transmission unit (MTU), in bytes. Sen The default size of the UDP packet used for transport, including all possible headers (Ethernet, IP and UDP), is 1500 bytes. -Refer to `SRTO_MSS` in [API.md](API.md). +Refer to `SRTO_MSS` in [SRT API Socket Options](API-socket-options.md). #### pktSndBuf @@ -727,9 +727,9 @@ that is received late. SRT group statistics are implemented for SRT Connection Bonding feature and available since SRT v1.5.0. Check the following documentation and code examples for details: -- [Introduction in SRT Connection Bonding feature](https://github.com/Haivision/srt/blob/master/docs/bonding-intro.md), -- [The concept of socket groups](https://github.com/Haivision/srt/blob/master/docs/socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, -- Check also [API](https://github.com/Haivision/srt/blob/master/docs/API.md) and [API functions](https://github.com/Haivision/srt/blob/master/docs/API-functions.md) documentation for Connection Bonding related updates, +- [Introduction in SRT Connection Bonding feature](../bonding-intro.md), +- [The concept of socket groups](../socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, +- Check also [SRT API](API.md) and [SRT API Functions](API-functions.md) documentation for Connection Bonding related updates, - Code examples: simple [client](https://github.com/Haivision/srt/blob/master/examples/test-c-client-bonding.c) and [server](https://github.com/Haivision/srt/blob/master/examples/test-c-server-bonding.c) implementation. `srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [API functions](API-functions.md) for usage instructions. @@ -768,7 +768,7 @@ This value counts every *original* DATA packet sent over the network for the fir This statistic does not count retransmitted DATA packets that are individual per socket connection within the group. See the corresponding [pktRetransTotal](#pktRetransTotal) socket statistic. -If the `SRTO_PACKETFILTER` socket option is enabled (refer to [API.md](API.md)), this statistic does not count packet filter control packets that are individual per socket connection within the group. See the corresponding [pktSndFilterExtraTotal](#pktSndFilterExtraTotal) socket statistic. +If the `SRTO_PACKETFILTER` socket option is enabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic does not count packet filter control packets that are individual per socket connection within the group. See the corresponding [pktSndFilterExtraTotal](#pktSndFilterExtraTotal) socket statistic. #### pktRecvUniqueTotal diff --git a/docs/AccessControl.md b/docs/AccessControl.md index df35e662c..402f90624 100644 --- a/docs/AccessControl.md +++ b/docs/AccessControl.md @@ -6,7 +6,7 @@ One type of information that can be interchanged when a connection is being established in SRT is "Stream ID", which can be used in a caller-listener connection layout. This is a string of maximum 512 characters set on the caller side. It can be retrieved at the listener side on the newly accepted socket -through a socket option (see `SRTO_STREAMID` in [API.md](API.md)). +through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](API/API-socket-options.md)). As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..ed2ad475c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +# Documentation Overview + +## SRT API Documents + +| Folder Name | File Name | Description | Refer as | +| :---------: | ------------------------------------------------------ | --------------------------------------------------- | ---------------------- | +| API | [API.md](API/API.md) | Detailed description of the SRT C API | SRT API | +| API | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions | SRT API Functions | +| API | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API | SRT API Socket Options | +| API | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics | SRT Statistics | diff --git a/docs/handshake.md b/docs/handshake.md index 5c16d07e7..756c538e4 100644 --- a/docs/handshake.md +++ b/docs/handshake.md @@ -1540,7 +1540,7 @@ application should set it on a Caller socket using the `SRTO_STREAMID` option. Upon connection, the accepted socket on the Listener side will have exactly the same value set, and it can be retrieved using the same option. For more details about the prospective use of this option, please refer to the -[API description document](API.md) and [SRT Access Control guidelines](AccessControl.md). +[SRT API Socket Options](API/API-socket-options.md) and [SRT Access Control guidelines](AccessControl.md). [Return to top of page](#srt-handshake) From e328bec1fb1dd9b3356693f7e175b19ac91fd262 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Tue, 30 Mar 2021 16:47:17 +0800 Subject: [PATCH 003/683] [core] Fix typo of variable name --- srtcore/epoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index c4c31782f..5f4992851 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -608,7 +608,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd { #ifdef LINUX const int max_events = ed.m_sLocals.size(); - SRT_ASSERT(max_event > 0); + SRT_ASSERT(max_events > 0); epoll_event ev[max_events]; int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0); @@ -631,7 +631,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd #elif defined(BSD) || TARGET_OS_MAC struct timespec tmout = {0, 0}; const int max_events = ed.m_sLocals.size(); - SRT_ASSERT(max_event > 0); + SRT_ASSERT(max_events > 0); struct kevent ke[max_events]; int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout); From 50c83555cb7c1dd83cc765271a1a18d9b0e22e6c Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 30 Mar 2021 12:02:38 +0200 Subject: [PATCH 004/683] [core] Fixed wrong limitation on SRTO_FC option. (#1899) Fixed documentation. --- docs/API/API-socket-options.md | 2 +- srtcore/socketconfig.h | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2dfc3bd8f..a77b97cc2 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -394,7 +394,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | | `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -Flight Flag Size (maximum number of bytes that can be sent without +Flight Flag Size (maximum number of packets that can be sent without being acknowledged) [Return to list](#list-of-options) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index c41bf4caf..2b0c3943e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -185,7 +185,7 @@ struct CSrtConfig: CSrtMuxerConfig static const uint32_t COMM_DEF_STABILITY_TIMEOUT_US = 80 * 1000; // Mimimum recv flight flag size is 32 packets - static const int DEF_MAX_FLIGHT_PKT = 32; + static const int DEF_MIN_FLIGHT_PKT = 32; static const size_t MAX_SID_LENGTH = 512; static const size_t MAX_PFILTER_LENGTH = 64; static const size_t MAX_CONG_LENGTH = 16; @@ -418,7 +418,7 @@ struct CSrtConfigSetter if (fc < 1) throw CUDTException(MJ_NOTSUP, MN_INVAL); - co.iFlightFlagSize = std::min(fc, +co.DEF_MAX_FLIGHT_PKT); + co.iFlightFlagSize = std::max(fc, +co.DEF_MIN_FLIGHT_PKT); } }; @@ -447,11 +447,10 @@ struct CSrtConfigSetter // Mimimum recv buffer size is 32 packets const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; - // XXX This magic 32 deserves some constant - if (val > mssin_size * co.DEF_MAX_FLIGHT_PKT) + if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; else - co.iRcvBufSize = co.DEF_MAX_FLIGHT_PKT; + co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; // recv buffer MUST not be greater than FC size if (co.iRcvBufSize > co.iFlightFlagSize) From b4288ce503dcdeb26d5388fad19281cbd26a8eb5 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 30 Mar 2021 14:58:55 +0200 Subject: [PATCH 005/683] [doc] Removed misleading information about connect callback (#1882) in successful connection case --- docs/API/API-functions.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 9ed45dc7b..734ce6221 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -942,12 +942,6 @@ resolved. When all links fail, you will only get a general error code for the group. This mechanism allows you to get individual errors for particular member connection failures. -You can also use this mechanism as an alternative method for a single-socket -connection in non-blocking mode to trigger an action when the connection -process is finished. It is recommended, however, that you use this callback -only to collect failure information, as the call will happen in one of the -internal SRT threads. - **Arguments**: * [`u`](#u): Socket or group that will be used for connecting and for which the hook is installed From 26c07c547ec6c456e82e04aea0cb932d03e72f4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 10:25:11 +0200 Subject: [PATCH 006/683] [core] Made CEpoll::m_EPollLock mutable --- srtcore/epoll.cpp | 2 +- srtcore/epoll.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 5f4992851..71945bdbd 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -808,7 +808,7 @@ int CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut, boo return 0; } -bool CEPoll::empty(CEPollDesc& d) +bool CEPoll::empty(const CEPollDesc& d) const { ScopedLock lg (m_EPollLock); return d.watch_empty(); diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 23b6c0065..3786137d5 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -425,7 +425,7 @@ friend class CRendezvousQueue; int swait(CEPollDesc& d, fmap_t& st, int64_t msTimeOut, bool report_by_exception = true); /// Empty subscription check - for internal use only. - bool empty(CEPollDesc& d); + bool empty(const CEPollDesc& d) const; /// Reports which events are ready on the given socket. /// @param mp socket event map retirned by `swait` @@ -486,7 +486,7 @@ friend class CRendezvousQueue; srt::sync::Mutex m_SeedLock; std::map m_mPolls; // all epolls - srt::sync::Mutex m_EPollLock; + mutable srt::sync::Mutex m_EPollLock; }; #if ENABLE_HEAVY_LOGGING From 262fe21b79e8a3d5d997d969611b7e0a8ff1f958 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 10:25:43 +0200 Subject: [PATCH 007/683] [core] sendBackup: logical OR instead of bitwise --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b13caad08..50b4cb6ca 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4193,7 +4193,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // is performed, and this one will result in none-write-ready, this will // be fixed just after returning from this function. - ready_again = ready_again | d->ps->writeReady(); + ready_again = ready_again || d->ps->writeReady(); } w_mc.grpdata_size = i; From a499c42356f86533e5d79a7c37395c51e2565943 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 16:58:02 +0200 Subject: [PATCH 008/683] [core] SocketData moved to group_common.h (#1907) (Refactoring) --- srtcore/api.cpp | 6 ++-- srtcore/api.h | 4 +-- srtcore/core.cpp | 13 +++++---- srtcore/filelist.maf | 2 ++ srtcore/group.cpp | 35 ----------------------- srtcore/group.h | 24 ++-------------- srtcore/group_common.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ srtcore/group_common.h | 60 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 138 insertions(+), 68 deletions(-) create mode 100644 srtcore/group_common.cpp create mode 100644 srtcore/group_common.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4293d8021..3b8f81ef1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1417,7 +1417,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar // Do it after setting all stored options, as some of them may // influence some group data. - CUDTGroup::SocketData data = g.prepareData(ns); + srt::groups::SocketData data = srt::groups::prepareSocketData(ns); if (targets[tii].token != -1) { // Reuse the token, if specified by the caller @@ -3228,7 +3228,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Check if the socket already is in the group - CUDTGroup::SocketData* f; + srt::groups::SocketData* f; if (g->contains(socket, (f))) { // XXX This is internal error. Report it, but continue @@ -3237,7 +3237,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) s->m_GroupOf = g; return 0; } - s->m_GroupMemberData = g->add(g->prepareData(s)); + s->m_GroupMemberData = g->add(srt::groups::prepareSocketData(s)); s->m_GroupOf = g; return 0; diff --git a/srtcore/api.h b/srtcore/api.h index 35f215ca3..cd467a978 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -117,8 +117,8 @@ class CUDTSocket SRTSOCKET m_PeerID; //< peer socket ID #if ENABLE_EXPERIMENTAL_BONDING - CUDTGroup::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member - CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't + srt::groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member + CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't #endif int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 5a1c62209..fc204816d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -79,6 +79,7 @@ modified by #endif using namespace std; +using namespace srt; using namespace srt::sync; using namespace srt_logging; @@ -3020,7 +3021,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } - CUDTGroup::SocketData* f = m_parent->m_GroupMemberData; + srt::groups::SocketData* f = m_parent->m_GroupMemberData; f->weight = link_weight; f->agent = m_parent->m_SelfAddr; @@ -3119,7 +3120,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l // Copy of addSocketToGroup. No idea how many parts could be common, not much. // Check if the socket already is in the group - CUDTGroup::SocketData* f; + srt::groups::SocketData* f; if (gp->contains(m_SocketID, (f))) { // XXX This is internal error. Report it, but continue @@ -3130,7 +3131,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l return 0; } - s->m_GroupMemberData = gp->add(gp->prepareData(s)); + s->m_GroupMemberData = gp->add(groups::prepareSocketData(s)); s->m_GroupOf = gp; // Record the remote address in the group data. @@ -4550,7 +4551,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE"); - CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = m_parent->m_GroupMemberData; gi->sndstate = SRT_GST_IDLE; gi->rcvstate = SRT_GST_IDLE; gi->laststatus = SRTS_CONNECTED; @@ -9294,7 +9295,7 @@ int CUDT::processData(CUnit* in_unit) if (m_parent->m_GroupOf) { ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); - CUDTGroup::SocketData* gi = m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long @@ -9793,7 +9794,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) if (self->m_parent->m_GroupOf) { - CUDTGroup::SocketData* gi = self->m_parent->m_GroupMemberData; + srt::groups::SocketData* gi = self->m_parent->m_GroupMemberData; if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index bdd57e789..4064c265f 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -28,6 +28,7 @@ sync.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp +group_common.cpp SOURCES - !ENABLE_STDCXX_SYNC sync_posix.cpp @@ -73,3 +74,4 @@ window.h PRIVATE HEADERS - ENABLE_EXPERIMENTAL_BONDING group.h +group_common.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 50b4cb6ca..4c8feff31 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -244,41 +244,6 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) return &*end; } -CUDTGroup::SocketData CUDTGroup::prepareData(CUDTSocket* s) -{ - // This uses default SRT_GST_BROKEN because when the group operation is done, - // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is - // recognized as an initial state of the fresh added socket to the group, - // so some "initial configuration" must be done on it, after which it's - // turned into SRT_GST_RUNNING, that is, it's treated as all others. When - // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned - // up, however, unless the status is simultaneously SRTS_BROKEN. - - // The order of operations is then: - // - add the socket to the group in this "broken" initial state - // - connect the socket (or get it extracted from accept) - // - update the socket state (should be SRTS_CONNECTED) - // - once the connection is established (may take time with connect), set SRT_GST_IDLE - // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING - SocketData sd = { - s->m_SocketID, - s, - -1, - SRTS_INIT, - SRT_GST_BROKEN, - SRT_GST_BROKEN, - -1, - -1, - sockaddr_any(), - sockaddr_any(), - false, - false, - false, - 0 // weight - }; - return sd; -} - CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_pGlobal(&CUDT::s_UDTUnited) , m_GroupID(-1) diff --git a/srtcore/group.h b/srtcore/group.h index 4f014a849..1477a4583 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -19,6 +19,7 @@ Written by #include "srt.h" #include "common.h" #include "packet.h" +#include "group_common.h" #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; @@ -31,6 +32,7 @@ class CUDTGroup typedef srt::sync::steady_clock::time_point time_point; typedef srt::sync::steady_clock::duration duration; typedef srt::sync::steady_clock steady_clock; + typedef srt::groups::SocketData SocketData; public: typedef SRT_MEMBERSTATUS GroupState; @@ -57,26 +59,6 @@ class CUDTGroup static int32_t s_tokenGen; static int32_t genToken() { ++s_tokenGen; if (s_tokenGen < 0) s_tokenGen = 0; return s_tokenGen;} - struct SocketData - { - SRTSOCKET id; - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; - }; - struct ConfigItem { SRT_SOCKOPT so; @@ -125,8 +107,6 @@ class CUDTGroup CUDTGroup(SRT_GROUP_TYPE); ~CUDTGroup(); - static SocketData prepareData(CUDTSocket* s); - SocketData* add(SocketData data); struct HaveID diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp new file mode 100644 index 000000000..513e5065c --- /dev/null +++ b/srtcore/group_common.cpp @@ -0,0 +1,62 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#include "platform_sys.h" + +#include "group_common.h" +#include "api.h" + +namespace srt +{ + namespace groups + { + + SocketData prepareSocketData(CUDTSocket* s) + { + // This uses default SRT_GST_BROKEN because when the group operation is done, + // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is + // recognized as an initial state of the fresh added socket to the group, + // so some "initial configuration" must be done on it, after which it's + // turned into SRT_GST_RUNNING, that is, it's treated as all others. When + // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned + // up, however, unless the status is simultaneously SRTS_BROKEN. + + // The order of operations is then: + // - add the socket to the group in this "broken" initial state + // - connect the socket (or get it extracted from accept) + // - update the socket state (should be SRTS_CONNECTED) + // - once the connection is established (may take time with connect), set SRT_GST_IDLE + // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING + SocketData sd = { + s->m_SocketID, + s, + -1, + SRTS_INIT, + SRT_GST_BROKEN, + SRT_GST_BROKEN, + -1, + -1, + sockaddr_any(), + sockaddr_any(), + false, + false, + false, + 0 // weight + }; + return sd; + } + + } // namespace groups +} // namespace srt diff --git a/srtcore/group_common.h b/srtcore/group_common.h new file mode 100644 index 000000000..13ef4ad4a --- /dev/null +++ b/srtcore/group_common.h @@ -0,0 +1,60 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#ifndef INC_SRT_GROUP_COMMON_H +#define INC_SRT_GROUP_COMMON_H + +#include "srt.h" +#include "common.h" +#include "core.h" + +#include + +namespace srt +{ + namespace groups + { + typedef SRT_MEMBERSTATUS GroupState; + + struct SocketData + { + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; + }; + + SocketData prepareSocketData(CUDTSocket* s); + + typedef std::list group_t; + typedef group_t::iterator gli_t; + + } // namespace groups +} // namespace srt + + +#endif // INC_SRT_GROUP_COMMON_H From 9cbf82e921b96e5c9372415e6888f5b836d750b1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 31 Mar 2021 11:18:02 +0200 Subject: [PATCH 009/683] [core] Added SRT_STATIC_ASSERT macro --- srtcore/common.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/srtcore/common.h b/srtcore/common.h index 32be66179..6c9948351 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -78,7 +78,6 @@ modified by #define NET_ERROR WSAGetLastError() #endif - #ifdef _DEBUG #include #define SRT_ASSERT(cond) assert(cond) @@ -86,6 +85,12 @@ modified by #define SRT_ASSERT(cond) #endif +#if HAVE_FULL_CXX11 +#define SRT_STATIC_ASSERT(cond, msg) static_assert(cond, msg) +#else +#define SRT_STATIC_ASSERT(cond, msg) +#endif + #include // Class CUDTException exposed for C++ API. From 60ae6e56014b5ee48c8e25eda4d7fcc2e28f79cc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 17:33:13 +0200 Subject: [PATCH 010/683] [apps] Added CSV and JSON stats byteAvailSndBuf, msSndBuf, byteAvailRcvBuf, msRcvBuf, msRcvTsbPdDelay --- apps/apputil.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 7d01bd9c4..0afe40cd5 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -391,10 +391,10 @@ struct SrtStatsTableInit STAT(SEND, bytes, byteSent); STAT(SEND, bytesUnique, byteSentUnique); STAT(SEND, bytesDropped, byteSndDrop); + STAT(SEND, byteAvailBuf, byteAvailSndBuf); + STAT(SEND, msBuf, msSndBuf); STAT(SEND, mbitRate, mbpsSendRate); STAT(SEND, sendPeriod, usPktSndPeriod); - //STAT(SEND, msAvgResponseTime, msAvgResponseTime); - //STAT(SEND, msMaxResponseTime, msMaxResponseTime); STAT(RECV, packets, pktRecv); STAT(RECV, packetsUnique, pktRecvUnique); @@ -409,7 +409,10 @@ struct SrtStatsTableInit STAT(RECV, bytesUnique, byteRecvUnique); STAT(RECV, bytesLost, byteRcvLoss); STAT(RECV, bytesDropped, byteRcvDrop); + STAT(RECV, byteAvailBuf, byteAvailRcvBuf); + STAT(RECV, msBuf, msRcvBuf); STAT(RECV, mbitRate, mbpsRecvRate); + STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); } } g_SrtStatsTableInit (g_SrtStatsTable); @@ -586,12 +589,8 @@ class SrtStatsCsv : public SrtStatsWriter output << endl; first_line_printed = true; } - int rcv_latency = 0; - int int_len = sizeof rcv_latency; - srt_getsockopt(sid, 0, SRTO_RCVLATENCY, &rcv_latency, &int_len); // Values - #ifdef HAS_PUT_TIME // HDR: Timepoint output << print_timestamp() << ","; @@ -685,4 +684,3 @@ SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) return SRTSTATS_PROFMAT_INVALID; } - From d5458908554b7ca6d5d2bc01b94a267a82ae62e9 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 6 Apr 2021 16:15:31 +0800 Subject: [PATCH 011/683] [docs] Fixed typos in LowLevelInfo (#1914) --- docs/LowLevelInfo.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/LowLevelInfo.md b/docs/LowLevelInfo.md index b3793dbf5..f7d1c1217 100644 --- a/docs/LowLevelInfo.md +++ b/docs/LowLevelInfo.md @@ -2,9 +2,9 @@ ## Introduction -This documnent will contain loose information for various topis referring to -the SRT source code describing some cross-source analysis that would be -unobvious for a source code reviewer. It's not a complete documentation of +This document contains information on various topics related to +the SRT source code, including descriptions of some cross-source analysis that would +not be obvious for a source code reviewer. It's not a complete documentation of anything, rather a collection of various kind of information retrieved during development and even reverse engineering. @@ -236,4 +236,3 @@ CSndQueue::worker CUDT::packData ``` - From f31f1fbae6c52c7dd2a0368cf8d4afdf0e1ab026 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 1 Apr 2021 17:45:08 +0200 Subject: [PATCH 012/683] [core] Applied clang-format on buffer.h. Fixed Doxygen comments alignment --- srtcore/buffer.h | 839 ++++++++++++++++++++++------------------------- 1 file changed, 397 insertions(+), 442 deletions(-) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 3963d3432..f0a499fd4 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -53,7 +53,6 @@ modified by #ifndef INC_SRT_BUFFER_H #define INC_SRT_BUFFER_H - #include "udt.h" #include "list.h" #include "queue.h" @@ -82,7 +81,8 @@ class AvgBufSize : m_dBytesCountMAvg(0.0) , m_dCountMAvg(0.0) , m_dTimespanMAvg(0.0) - { } + { + } public: bool isTimeToUpdate(const time_point& now) const; @@ -100,531 +100,486 @@ class AvgBufSize double m_dTimespanMAvg; }; - class CSndBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef srt::sync::steady_clock::time_point time_point; + typedef srt::sync::steady_clock::duration duration; public: + // XXX There's currently no way to access the socket ID set for + // whatever the buffer is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the buffer is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - CSndBuffer(int size = 32, int mss = 1500); - ~CSndBuffer(); + CSndBuffer(int size = 32, int mss = 1500); + ~CSndBuffer(); public: - - /// Insert a user buffer into the sending list. - /// For @a w_mctrl the following fields are used: - /// INPUT: - /// - msgttl: timeout for retransmitting the message, if lost - /// - inorder: request to deliver the message in order of sending - /// - srctime: local time as a base for packet's timestamp (0 if unused) - /// - pktseq: sequence number to be stamped on the packet (-1 if unused) - /// - msgno: message number to be stamped on the packet (-1 if unused) - /// OUTPUT: - /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) - /// - pktseq: sequence number to be stamped on the next packet - /// - msgno: message number stamped on the packet - /// @param [in] data pointer to the user data block. - /// @param [in] len size of the block. - /// @param [inout] w_mctrl Message control data - void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); - - /// Read a block of data from file and insert it into the sending list. - /// @param [in] ifs input file stream. - /// @param [in] len size of the block. - /// @return actual size of data added from the file. - - int addBufferFromFile(std::fstream& ifs, int len); - - /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] data the pointer to the data position. - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [in] kflags Odd|Even crypto key flag - /// @return Actual length of data read. - - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); - - /// Find data position to pack a DATA packet for a retransmission. - /// @param [out] data the pointer to the data position. - /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read. - - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); - - /// Get the time of the last retransmission (if any) of the DATA packet. - /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// - /// @return Last time of the last retransmission event for the corresponding DATA packet. - - time_point getPacketRexmitTime(const int offset); - - /// Update the ACK point and may release/unmap/return the user data according to the flag. - /// @param [in] offset number of packets acknowledged. - - int32_t getMsgNoAt(const int offset); - - void ackData(int offset); - - /// Read size of data still in the sending list. - /// @return Current size of the data in the sending list. - - int getCurrBufSize() const; - - int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); - - void updAvgBufSize(const time_point& time); - int getAvgBufSize(int& bytes, int& timespan); - int getCurrBufSize(int& bytes, int& timespan); - - uint64_t getInRatePeriod() const { return m_InRatePeriod; } - - /// Retrieve input bitrate in bytes per second - int getInputRate() const { return m_iInRateBps; } - - /// Update input rate calculation. - /// @param [in] time current time in microseconds - /// @param [in] pkts number of packets newly added to the buffer - /// @param [in] bytes number of payload bytes in those newly added packets - /// - /// @return Current size of the data in the sending list. - void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); - - - void resetInputRateSmpPeriod(bool disable = false) - { - setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); - } + /// Insert a user buffer into the sending list. + /// For @a w_mctrl the following fields are used: + /// INPUT: + /// - msgttl: timeout for retransmitting the message, if lost + /// - inorder: request to deliver the message in order of sending + /// - srctime: local time as a base for packet's timestamp (0 if unused) + /// - pktseq: sequence number to be stamped on the packet (-1 if unused) + /// - msgno: message number to be stamped on the packet (-1 if unused) + /// OUTPUT: + /// - srctime: local time stamped on the packet (same as input, if input wasn't 0) + /// - pktseq: sequence number to be stamped on the next packet + /// - msgno: message number stamped on the packet + /// @param [in] data pointer to the user data block. + /// @param [in] len size of the block. + /// @param [inout] w_mctrl Message control data + void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); + + /// Read a block of data from file and insert it into the sending list. + /// @param [in] ifs input file stream. + /// @param [in] len size of the block. + /// @return actual size of data added from the file. + int addBufferFromFile(std::fstream& ifs, int len); + + /// Find data position to pack a DATA packet from the furthest reading point. + /// @param [out] data the pointer to the data position. + /// @param [out] msgno message number of the packet. + /// @param [out] origintime origin time stamp of the message + /// @param [in] kflags Odd|Even crypto key flag + /// @return Actual length of data read. + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + + /// Find data position to pack a DATA packet for a retransmission. + /// @param [out] data the pointer to the data position. + /// @param [in] offset offset from the last ACK point (backward sequence number difference) + /// @param [out] msgno message number of the packet. + /// @param [out] origintime origin time stamp of the message + /// @param [out] msglen length of the message + /// @return Actual length of data read. + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + + /// Get the time of the last retransmission (if any) of the DATA packet. + /// @param [in] offset offset from the last ACK point (backward sequence number difference) + /// + /// @return Last time of the last retransmission event for the corresponding DATA packet. + time_point getPacketRexmitTime(const int offset); + + /// Update the ACK point and may release/unmap/return the user data according to the flag. + /// @param [in] offset number of packets acknowledged. + int32_t getMsgNoAt(const int offset); + + void ackData(int offset); + + /// Read size of data still in the sending list. + /// @return Current size of the data in the sending list. + int getCurrBufSize() const; + + int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + + void updAvgBufSize(const time_point& time); + int getAvgBufSize(int& bytes, int& timespan); + int getCurrBufSize(int& bytes, int& timespan); + + uint64_t getInRatePeriod() const { return m_InRatePeriod; } + + /// Retrieve input bitrate in bytes per second + int getInputRate() const { return m_iInRateBps; } + + /// Update input rate calculation. + /// @param [in] time current time in microseconds + /// @param [in] pkts number of packets newly added to the buffer + /// @param [in] bytes number of payload bytes in those newly added packets + /// + /// @return Current size of the data in the sending list. + void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0); + + void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); } private: - void increase(); - void setInputRateSmpPeriod(int period); - - struct Block; // Defined below - static time_point getSourceTime(const CSndBuffer::Block& block); + void increase(); + void setInputRateSmpPeriod(int period); -private: // Constants + struct Block; // Defined below + static time_point getSourceTime(const CSndBuffer::Block& block); - static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms - static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms - static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload +private: // Constants + static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms + static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms + static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - srt::sync::Mutex m_BufLock; // used to synchronize buffer operation + srt::sync::Mutex m_BufLock; // used to synchronize buffer operation - struct Block - { - char* m_pcData; // pointer to the data block - int m_iLength; // length of the block + struct Block + { + char* m_pcData; // pointer to the data block + int m_iLength; // length of the block - int32_t m_iMsgNoBitset; // message number - int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // original request time - time_point m_tsRexmitTime; // packet retransmission time - uint64_t m_llSourceTime_us; - int m_iTTL; // time to live (milliseconds) + int32_t m_iMsgNoBitset; // message number + int32_t m_iSeqNo; // sequence number for scheduling + time_point m_tsOriginTime; // original request time + time_point m_tsRexmitTime; // packet retransmission time + uint64_t m_llSourceTime_us; + int m_iTTL; // time to live (milliseconds) - Block* m_pNext; // next block + Block* m_pNext; // next block - int32_t getMsgSeq() - { - // NOTE: this extracts message ID with regard to REXMIT flag. - // This is valid only for message ID that IS GENERATED in this instance, - // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter - // for the peer that it uses LESS bits to represent the message. - return m_iMsgNoBitset & MSGNO_SEQ::mask; - } + int32_t getMsgSeq() + { + // NOTE: this extracts message ID with regard to REXMIT flag. + // This is valid only for message ID that IS GENERATED in this instance, + // not provided by the peer. This can be otherwise sent to the peer - it doesn't matter + // for the peer that it uses LESS bits to represent the message. + return m_iMsgNoBitset & MSGNO_SEQ::mask; + } - } *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; + } * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock; - // m_pBlock: The head pointer - // m_pFirstBlock: The first block - // m_pCurrBlock: The current block - // m_pLastBlock: The last block (if first == last, buffer is empty) + // m_pBlock: The head pointer + // m_pFirstBlock: The first block + // m_pCurrBlock: The current block + // m_pLastBlock: The last block (if first == last, buffer is empty) - struct Buffer - { - char* m_pcData; // buffer - int m_iSize; // size - Buffer* m_pNext; // next buffer - } *m_pBuffer; // physical buffer + struct Buffer + { + char* m_pcData; // buffer + int m_iSize; // size + Buffer* m_pNext; // next buffer + } * m_pBuffer; // physical buffer - int32_t m_iNextMsgNo; // next message number + int32_t m_iNextMsgNo; // next message number - int m_iSize; // buffer size (number of packets) - int m_iMSS; // maximum seqment/packet size + int m_iSize; // buffer size (number of packets) + int m_iMSS; // maximum seqment/packet size - int m_iCount; // number of used blocks + int m_iCount; // number of used blocks - int m_iBytesCount; // number of payload bytes in queue - time_point m_tsLastOriginTime; + int m_iBytesCount; // number of payload bytes in queue + time_point m_tsLastOriginTime; - AvgBufSize m_mavg; + AvgBufSize m_mavg; - int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime - int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime - time_point m_tsInRateStartTime; - uint64_t m_InRatePeriod; // usec - int m_iInRateBps; // Input Rate in Bytes/sec - int m_iAvgPayloadSz; // Average packet payload size + int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime + int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime + time_point m_tsInRateStartTime; + uint64_t m_InRatePeriod; // usec + int m_iInRateBps; // Input Rate in Bytes/sec + int m_iAvgPayloadSz; // Average packet payload size private: - CSndBuffer(const CSndBuffer&); - CSndBuffer& operator=(const CSndBuffer&); + CSndBuffer(const CSndBuffer&); + CSndBuffer& operator=(const CSndBuffer&); }; //////////////////////////////////////////////////////////////////////////////// - class CRcvBuffer { typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef srt::sync::steady_clock::duration duration; public: - // XXX There's currently no way to access the socket ID set for // whatever the queue is currently working for. Required to find // some way to do this, possibly by having a "reverse pointer". // Currently just "unimplemented". std::string CONID() const { return ""; } - static const int DEFAULT_SIZE = 65536; - /// Construct the buffer. - /// @param [in] queue CUnitQueue that actually holds the units (packets) - /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); - ~CRcvBuffer(); - + static const int DEFAULT_SIZE = 65536; + /// Construct the buffer. + /// @param [in] queue CUnitQueue that actually holds the units (packets) + /// @param [in] bufsize_pkts in units (packets) + CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + ~CRcvBuffer(); public: - - /// Write data into the buffer. - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. - /// @return 0 is success, -1 if data is repeated. - - int addData(CUnit* unit, int offset); - - /// Read data into a user buffer. - /// @param [in] data pointer to user buffer. - /// @param [in] len length of user buffer. - /// @return size of data read. - - int readBuffer(char* data, int len); - - /// Read data directly into file. - /// @param [in] file C++ file stream. - /// @param [in] len expected length of data to write into the file. - /// @return size of data read. - - int readBufferToFile(std::fstream& ofs, int len); - - /// Update the ACK point of the buffer. - /// @param [in] len number of units to be acknowledged. - /// @return 1 if a user buffer is fulfilled, otherwise 0. - - int ackData(int len); - - /// Query how many buffer space left for data receiving. - /// Actually only acknowledged packets, that are still in the buffer, - /// are considered to take buffer space. - /// - /// @return size of available buffer space (including user buffer) for data receiving. - /// Not counting unacknowledged packets. - - int getAvailBufSize() const; - - /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). - /// @return size of valid (continous) data for reading. - - int getRcvDataSize() const; - - /// Query how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - - int getRcvDataSize(int& bytes, int &spantime); - - /// Query a 1 sec moving average of how many data was received and acknowledged. - /// @param [out] bytes bytes - /// @param [out] spantime spantime - /// @return size in pkts of acked data. - - int getRcvAvgDataSize(int& bytes, int& spantime); - - /// Query how many data of the receive buffer is acknowledged. - /// @param [in] now current time in us. - /// @return none. - - void updRcvAvgDataSize(const time_point& now); - - /// Query the received average payload size. - /// @return size (bytes) of payload size - - unsigned getRcvAvgPayloadSize() const; - - - /// Mark the message to be dropped from the message list. - /// @param [in] msgno message number. - /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the msgno value) - - void dropMsg(int32_t msgno, bool using_rexmit_flag); - - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @return actuall size of data read. - - int readMsg(char* data, int len); + /// Write data into the buffer. + /// @param [in] unit pointer to a data unit containing new packet + /// @param [in] offset offset from last ACK point. + /// @return 0 is success, -1 if data is repeated. + int addData(CUnit* unit, int offset); + + /// Read data into a user buffer. + /// @param [in] data pointer to user buffer. + /// @param [in] len length of user buffer. + /// @return size of data read. + int readBuffer(char* data, int len); + + /// Read data directly into file. + /// @param [in] file C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. + int readBufferToFile(std::fstream& ofs, int len); + + /// Update the ACK point of the buffer. + /// @param [in] len number of units to be acknowledged. + /// @return 1 if a user buffer is fulfilled, otherwise 0. + int ackData(int len); + + /// Query how many buffer space left for data receiving. + /// Actually only acknowledged packets, that are still in the buffer, + /// are considered to take buffer space. + /// + /// @return size of available buffer space (including user buffer) for data receiving. + /// Not counting unacknowledged packets. + int getAvailBufSize() const; + + /// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now). + /// @return size of valid (continous) data for reading. + int getRcvDataSize() const; + + /// Query how many data was received and acknowledged. + /// @param [out] bytes bytes + /// @param [out] spantime spantime + /// @return size in pkts of acked data. + int getRcvDataSize(int& bytes, int& spantime); + + /// Query a 1 sec moving average of how many data was received and acknowledged. + /// @param [out] bytes bytes + /// @param [out] spantime spantime + /// @return size in pkts of acked data. + int getRcvAvgDataSize(int& bytes, int& spantime); + + /// Query how many data of the receive buffer is acknowledged. + /// @param [in] now current time in us. + /// @return none. + void updRcvAvgDataSize(const time_point& now); + + /// Query the received average payload size. + /// @return size (bytes) of payload size + unsigned getRcvAvgPayloadSize() const; + + /// Mark the message to be dropped from the message list. + /// @param [in] msgno message number. + /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the + /// msgno value) + void dropMsg(int32_t msgno, bool using_rexmit_flag); + + /// read a message. + /// @param [out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @return actuall size of data read. + int readMsg(char* data, int len); #if ENABLE_HEAVY_LOGGING - void readMsgHeavyLogging(int p); + void readMsgHeavyLogging(int p); #endif - /// read a message. - /// @param [out] data buffer to write the message into. - /// @param [in] len size of the buffer. - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// @return actuall size of data read. - - int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto); - /// Query if data is ready to read (tsbpdtime <= now if TsbPD is active). - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay - /// of next packet in recv buffer, ready or not. - /// @param [out] curpktseq Sequence number of the packet if there is one ready to play - /// @return true if ready to play, false otherwise (tsbpdtime may be !0 in - /// both cases). - - bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); + /// read a message. + /// @param [out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay + /// @return actuall size of data read. + int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto); + + /// Query if data is ready to read (tsbpdtime <= now if TsbPD is active). + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay + /// of next packet in recv buffer, ready or not. + /// @param [out] curpktseq Sequence number of the packet if there is one ready to play + /// @return true if ready to play, false otherwise (tsbpdtime may be !0 in + /// both cases). + bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); #ifdef SRT_DEBUG_TSBPD_OUTJITTER - void debugTraceJitter(int64_t); + void debugTraceJitter(int64_t); #else - void debugTraceJitter(int64_t) {} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - - bool isRcvDataReady(); - bool isRcvDataAvailable() - { - return m_iLastAckPos != m_iStartPos; - } - CPacket* getRcvReadyPacket(int32_t seqdistance); - - /// Set TimeStamp-Based Packet Delivery Rx Mode - /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay - /// @param [in] delay aggreed TsbPD delay - /// @return 0 + void debugTraceJitter(int64_t) {} +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ - int setRcvTsbPdMode(const time_point& timebase, const duration& delay); + bool isRcvDataReady(); + bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } + CPacket* getRcvReadyPacket(int32_t seqdistance); - /// Add packet timestamp for drift caclculation and compensation - /// @param [in] timestamp packet time stamp - /// @param [ref] lock Mutex that should be locked for the operation + /// Set TimeStamp-Based Packet Delivery Rx Mode + /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay + /// @param [in] delay aggreed TsbPD delay + /// @return 0 + int setRcvTsbPdMode(const time_point& timebase, const duration& delay); - bool addRcvTsbPdDriftSample(uint32_t timestamp, srt::sync::Mutex& mutex_to_lock, - duration& w_udrift, time_point& w_newtimebase); + /// Add packet timestamp for drift caclculation and compensation + /// @param [in] timestamp packet time stamp + /// @param [ref] lock Mutex that should be locked for the operation + bool addRcvTsbPdDriftSample(uint32_t timestamp, + srt::sync::Mutex& mutex_to_lock, + duration& w_udrift, + time_point& w_newtimebase); #ifdef SRT_DEBUG_TSBPD_DRIFT - void printDriftHistogram(int64_t iDrift); - void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg); + void printDriftHistogram(int64_t iDrift); + void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg); #endif - /// Get information on the 1st message in queue. - // Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none - /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) - /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets. - /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true - /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: - /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; - /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. - - - bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); - - /// Update the ACK point of the buffer. - /// @param [in] len size of data to be skip & acknowledged. - - void skipData(int len); + /// Get information on the 1st message in queue. + // Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 + /// if none + /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) + /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by + /// missing packets. + /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true + /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: + /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; + /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. + bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); + + /// Update the ACK point of the buffer. + /// @param [in] len size of data to be skip & acknowledged. + void skipData(int len); #if ENABLE_HEAVY_LOGGING - void reportBufferStats() const; // Heavy logging Debug only + void reportBufferStats() const; // Heavy logging Debug only #endif - bool empty() const - { - // This will not always return the intended value, - // that is, it may return false when the buffer really is - // empty - but it will return true then in one of next calls. - // This function will be always called again at some point - // if it returned false, and on true the connection - // is going to be broken - so this behavior is acceptable. - return m_iStartPos == m_iLastAckPos; - } - bool full() const { return m_iStartPos == (m_iLastAckPos+1)%m_iSize; } - int capacity() const { return m_iSize; } - + bool empty() const + { + // This will not always return the intended value, + // that is, it may return false when the buffer really is + // empty - but it will return true then in one of next calls. + // This function will be always called again at some point + // if it returned false, and on true the connection + // is going to be broken - so this behavior is acceptable. + return m_iStartPos == m_iLastAckPos; + } + bool full() const { return m_iStartPos == (m_iLastAckPos + 1) % m_iSize; } + int capacity() const { return m_iSize; } private: - /// This gives up unit at index p. The unit is given back to the - /// free unit storage for further assignment for the new incoming - /// data. - size_t freeUnitAt(size_t p) - { - CUnit* u = m_pUnit[p]; - m_pUnit[p] = NULL; - size_t rmbytes = u->m_Packet.getLength(); - m_pUnitQueue->makeUnitFree(u); - return rmbytes; - } - - /// Adjust receive queue to 1st ready to play message (tsbpdtime < now). - // Parameters (of the 1st packet queue, ready to play or not): - /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none - /// @retval true 1st packet ready to play without discontinuity (no hole) - /// @retval false tsbpdtime = 0: no packet ready to play - - - bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); + /// This gives up unit at index p. The unit is given back to the + /// free unit storage for further assignment for the new incoming + /// data. + size_t freeUnitAt(size_t p) + { + CUnit* u = m_pUnit[p]; + m_pUnit[p] = NULL; + size_t rmbytes = u->m_Packet.getLength(); + m_pUnitQueue->makeUnitFree(u); + return rmbytes; + } + + /// Adjust receive queue to 1st ready to play message (tsbpdtime < now). + /// Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if + /// none + /// @retval true 1st packet ready to play without discontinuity (no hole) + /// @retval false tsbpdtime = 0: no packet ready to play + bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: + /// Get packet delivery local time base (adjusted for wrap around) + /// (Exposed as used publicly in logs) + /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min + /// @return local delivery time (usec) + time_point getTsbPdTimeBase(uint32_t timestamp_us); - // (This is exposed as used publicly in logs) - /// Get packet delivery local time base (adjusted for wrap around) - /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min - /// @return local delivery time (usec) - - time_point getTsbPdTimeBase(uint32_t timestamp_us); - - int64_t getDrift() const { return m_DriftTracer.drift(); } + int64_t getDrift() const { return m_DriftTracer.drift(); } public: - int32_t getTopMsgno() const; + int32_t getTopMsgno() const; - // @return Wrap check value - bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); + // @return Wrap check value + bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); - void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); - void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); - time_point getPktTsbPdTime(uint32_t timestamp); - int debugGetSize() const; - time_point debugGetDeliveryTime(int offset); + void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); + void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); + time_point getPktTsbPdTime(uint32_t timestamp); + int debugGetSize() const; + time_point debugGetDeliveryTime(int offset); - size_t dropData(int len); + size_t dropData(int len); private: - int extractData(char *data, int len, int p, int q, bool passack); - bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto); + int extractData(char* data, int len, int p, int q, bool passack); + bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto); - /// Describes the state of the first N packets - std::string debugTimeState(size_t first_n_pkts) const; - - /// thread safe bytes counter of the Recv & Ack buffer - /// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true) - /// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer. - /// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer + /// Describes the state of the first N packets + std::string debugTimeState(size_t first_n_pkts) const; - void countBytes(int pkts, int bytes, bool acked = false); + /// thread safe bytes counter of the Recv & Ack buffer + /// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true) + /// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer. + /// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer + void countBytes(int pkts, int bytes, bool acked = false); private: - bool scanMsg(int& w_start, int& w_end, bool& w_passack); - - int shift(int basepos, int shift) const - { - return (basepos + shift) % m_iSize; - } - - // Simplified versions with ++ and --; avoid using division instruction - int shiftFwd(int basepos) const - { - if (++basepos == m_iSize) - return 0; - return basepos; - } - - int shiftBack(int basepos) const - { - if (basepos == 0) - return m_iSize-1; - return --basepos; - } + bool scanMsg(int& w_start, int& w_end, bool& w_passack); + + int shift(int basepos, int shift) const { return (basepos + shift) % m_iSize; } + + /// Simplified versions with ++ and --; avoid using division instruction + int shiftFwd(int basepos) const + { + if (++basepos == m_iSize) + return 0; + return basepos; + } + + int shiftBack(int basepos) const + { + if (basepos == 0) + return m_iSize - 1; + return --basepos; + } private: - CUnit** m_pUnit; // Array of pointed units collected in the buffer - const int m_iSize; // Size of the internal array of CUnit* items - CUnitQueue* m_pUnitQueue; // the shared unit queue - - int m_iStartPos; // HEAD: first packet available for reading - int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable - // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 - int m_iMaxPos; // delta between acked-TAIL and reception-TAIL - - - int m_iNotch; // the starting read point of the first unit - // (this is required for stream reading mode; it's - // the position in the first unit in the list - // up to which data are already retrieved; - // in message reading mode it's unused and always 0) - - srt::sync::Mutex m_BytesCountLock; // used to protect counters operations - int m_iBytesCount; // Number of payload bytes in the buffer - int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer - int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - - bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode - duration m_tdTsbPdDelay; // aggreed delay - time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode - // Note: m_tsTsbPdTimeBase cumulates values from: - // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: - // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception - // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected - // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). - // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively - // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE - // once the value of average drift exceeds this value in whatever direction. - // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE - // - // XXX Application-supplied timestamps won't work therefore. This requires separate - // calculation of all these things above. - - bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around - static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec) - - /// Max drift (usec) above which TsbPD Time Offset is adjusted - static const int TSBPD_DRIFT_MAX_VALUE = 5000; - /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation - static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; - DriftTracer m_DriftTracer; - AvgBufSize m_mavg; + CUnit** m_pUnit; // Array of pointed units collected in the buffer + const int m_iSize; // Size of the internal array of CUnit* items + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartPos; // HEAD: first packet available for reading + int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable + // EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1 + int m_iMaxPos; // delta between acked-TAIL and reception-TAIL + + int m_iNotch; // the starting read point of the first unit + // (this is required for stream reading mode; it's + // the position in the first unit in the list + // up to which data are already retrieved; + // in message reading mode it's unused and always 0) + + srt::sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer + int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation + + bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode + duration m_tdTsbPdDelay; // aggreed delay + time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode + // Note: m_tsTsbPdTimeBase cumulates values from: + // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: + // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception + // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected + // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). + // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively + // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE + // once the value of average drift exceeds this value in whatever direction. + // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE + // + // XXX Application-supplied timestamps won't work therefore. This requires separate + // calculation of all these things above. + + bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around + static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) + + /// Max drift (usec) above which TsbPD Time Offset is adjusted + static const int TSBPD_DRIFT_MAX_VALUE = 5000; + /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation + static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; + DriftTracer m_DriftTracer; + AvgBufSize m_mavg; #ifdef SRT_DEBUG_TSBPD_DRIFT - int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) - int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) - int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram - static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram -#endif /* SRT_DEBUG_TSBPD_DRIFT */ + int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) + int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) + int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram + static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram +#endif /* SRT_DEBUG_TSBPD_DRIFT */ #ifdef SRT_DEBUG_TSBPD_OUTJITTER - unsigned long m_ulPdHisto[4][10]; + unsigned long m_ulPdHisto[4][10]; #endif /* SRT_DEBUG_TSBPD_OUTJITTER */ private: - CRcvBuffer(); - CRcvBuffer(const CRcvBuffer&); - CRcvBuffer& operator=(const CRcvBuffer&); + CRcvBuffer(); + CRcvBuffer(const CRcvBuffer&); + CRcvBuffer& operator=(const CRcvBuffer&); }; - #endif From 81d3b95d3f46e9961345c6567210ef1df4659454 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Thu, 8 Apr 2021 10:10:53 -0500 Subject: [PATCH 013/683] [core] Build fix: added ParseFilterConfig declaration (#1918) --- srtcore/packetfilter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index dac988102..545e38e02 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -210,4 +210,6 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer); inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); } inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); } +bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); + #endif From 4849c3c94a75b759f785b89c5a9481ec7a5603ef Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 7 Apr 2021 11:09:54 +0200 Subject: [PATCH 014/683] [core] SRTO_FC: reject values lower than 32 --- srtcore/socketconfig.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 2b0c3943e..29105808b 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -414,11 +414,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - int fc = cast_optval(optval, optlen); - if (fc < 1) + using namespace srt_logging; + const int fc = cast_optval(optval, optlen); + if (fc < co.DEF_MIN_FLIGHT_PKT) + { + LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); throw CUDTException(MJ_NOTSUP, MN_INVAL); + } - co.iFlightFlagSize = std::max(fc, +co.DEF_MIN_FLIGHT_PKT); + co.iFlightFlagSize = fc; } }; From b16b68a995d87bb57a6145776c84f113d2c19ee1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Apr 2021 11:19:10 +0200 Subject: [PATCH 015/683] [docs] Fix SRTO_MINVERSION: readable, default 1.0.0 --- docs/API/API-socket-options.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index a77b97cc2..14d30511e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -219,7 +219,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | | [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | @@ -784,12 +784,14 @@ SCTP protocol. | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0 | * | W | GSD | +| `SRTO_MINVERSION` | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | The minimum SRT version that is required from the peer. A connection to a peer that does not satisfy the minimum version requirement will be rejected. See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. +The default value is 0x010000 (SRT v1.0.0). + [Return to list](#list-of-options) --- From 8bf8c5538d0e10fa4f14638388e95ef8a718dcff Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Apr 2021 11:22:28 +0200 Subject: [PATCH 016/683] [docs] Improved SRTO_FC description Co-authored-by: stevomatthews Co-authored-by: cmollahan <43041498+cmollahan@users.noreply.github.com> --- docs/API/API-socket-options.md | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 14d30511e..e8e391ee9 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -394,8 +394,34 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | | `SRTO_FC` | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -Flight Flag Size (maximum number of packets that can be sent without -being acknowledged) +Flow Control limits the maximum number of packets "in flight" - payload (data) packets that were sent +but reception is not yet acknowledged with an ACK control packet. +It also includes data packets already received, but that can't be acknowledged due to loss of preceding data packet(s). +In other words, if a data packet with sequence number `A` was lost, then acknowledgement of the following `SRTO_FC` packets +is blocked until packet `A` is either successfully retransmitted or dropped by the +[Too-Late Packet Drop mechanism](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.6). +Thus the sender will have `SRTO_FC` packets in flight, and will not be allowed to send further data packets. +Therefore, when establishing the value of `SRTO_FC`, it is recommend taking into consideration possible delays due to packet loss and retransmission. + +There is a restriction that the receiver buffer size ([SRTO_RCVBUF](#SRTO_RCVBUF)) must not be greater than `SRTO_FC` +([#700](https://github.com/Haivision/srt/issues/700)). +Therefore, it is recommended to set the value of `SRTO_FC` first, and then the value of `SRTO_RCVBUF`. + +The default flow control window size is 25600 packets. It is approximately: +- 270 Mbits of payload in the default live streaming configuration with an SRT payload size of 1316 bytes; +- 300 Mbits of payload with an SRT payload size of 1456 bytes. + +The minimum number of packets in flight should be (assuming max payload size): +`FCmin = bps / 8 × RTTsec / (MSS - 44)`, +where +- `bps` - is the payload bitrate of the stream in bits per second; +- `RTTsec` - RTT of the network connection in seconds; +- `MSS` - Maximum segment size (aka MTU), see [SRTO_MSS](#SRTO_MSS); +- 44 - size of headers (20 bytes IPv4 + 8 bytes of UDP + 16 bytes of SRT packet header). + +To avoid blocking the sending of further packets in case of packet loss, the recommended flow control window is +`FC = bps / 8 × (RTTsec + latency_sec) / (MSS - 44)`, +where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCVLATENCY)) **in seconds**. [Return to list](#list-of-options) From bd55e2945b8f8943418fb496a30953f9796527ac Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 09:51:59 +0200 Subject: [PATCH 017/683] [core] Refact: moved code to processCtrlAckAck dedicated function --- srtcore/core.cpp | 146 +++++++++++++++++++++++------------------------ srtcore/core.h | 18 +++++- 2 files changed, 88 insertions(+), 76 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fc204816d..89cf09b16 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8034,6 +8034,77 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point leaveCS(m_StatsLock); } +void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) +{ + int32_t ack = 0; + + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, tsArrival); + + if (rtt == -1) + { + if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) + { + LOGC(inlog.Warn, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + return; + } + + LOGC(inlog.Error, + log << CONID() << "IPE: ACK record not found, can't estimate RTT " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iRTT << ")"); + return; + } + + if (rtt <= 0) + { + LOGC(inlog.Error, + log << CONID() << "IPE: invalid RTT estimate " << rtt + << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); + return; + } + + // If increasing delay is detected + // sendCtrl(UMSG_CGWARNING); + + // Calculate RTT (EWMA) on the receiver side + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + + updateCC(TEV_ACKACK, EventVariant(ack)); + + // This function will put a lock on m_RecvLock by itself, as needed. + // It must be done inside because this function reads the current time + // and if waiting for the lock has caused a delay, the time will be + // inaccurate. Additionally it won't lock if TSBPD mode is off, and + // won't update anything. Note that if you set TSBPD mode and use + // srt_recvfile (which doesn't make any sense), you'll have a deadlock. + if (m_config.bDriftTracer) + { + steady_clock::duration udrift(0); + steady_clock::time_point newtimebase; + const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, + (udrift), (newtimebase)); +#if ENABLE_EXPERIMENTAL_BONDING + if (drift_updated && m_parent->m_GroupOf) + { + ScopedLock glock(s_UDTUnited.m_GlobControlLock); + if (m_parent->m_GroupOf) + { + m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); + } + } +#endif + } + + // Update last ACK that has been received by the sender + if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) + m_iRcvLastAckAck = ack; +} + void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) { const int32_t* losslist = (int32_t*)(ctrlpkt.m_pcData); @@ -8184,7 +8255,6 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) m_iEXPCount = 1; const steady_clock::time_point currtime = steady_clock::now(); m_tsLastRspTime = currtime; - bool using_rexmit_flag = m_bPeerRexmitFlag; HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" @@ -8197,77 +8267,8 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement - { - int32_t ack = 0; - - // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair - const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, currtime); - - if (rtt == -1) - { - if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) - { - LOGC(inlog.Warn, - log << CONID() << "ACKACK out of order, skipping RTT calculation " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); - break; - } - - LOGC(inlog.Error, - log << CONID() << "IPE: ACK record not found, can't estimate RTT " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); - break; - } - - if (rtt <= 0) - { - LOGC(inlog.Error, - log << CONID() << "IPE: invalid RTT estimate " << rtt - << ", possible time shift. Clock: " << SRT_SYNC_CLOCK_STR); - break; - } - - // If increasing delay is detected - // sendCtrl(UMSG_CGWARNING); - - // Calculate RTT (EWMA) on the receiver side - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); - - updateCC(TEV_ACKACK, EventVariant(ack)); - - // This function will put a lock on m_RecvLock by itself, as needed. - // It must be done inside because this function reads the current time - // and if waiting for the lock has caused a delay, the time will be - // inaccurate. Additionally it won't lock if TSBPD mode is off, and - // won't update anything. Note that if you set TSBPD mode and use - // srt_recvfile (which doesn't make any sense), you'll have a deadlock. - if (m_config.bDriftTracer) - { - steady_clock::duration udrift(0); - steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, - (udrift), (newtimebase)); -#if ENABLE_EXPERIMENTAL_BONDING - if (drift_updated && m_parent->m_GroupOf) - { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); - if (m_parent->m_GroupOf) - { - m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); - } - } -#endif - } - - // Update last ACK that has been received by the sender - if (CSeqNo::seqcmp(ack, m_iRcvLastAckAck) > 0) - m_iRcvLastAckAck = ack; - + processCtrlAckAck(ctrlpkt, currtime); break; - } case UMSG_LOSSREPORT: // 011 - Loss Report processCtrlLossReport(ctrlpkt); @@ -8284,9 +8285,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_KEEPALIVE: // 001 - Keep-alive - handleKeepalive(ctrlpkt.m_pcData, ctrlpkt.getLength()); - break; case UMSG_HANDSHAKE: // 000 - Handshake @@ -8416,6 +8415,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_DROPREQ: // 111 - Msg drop request { + const bool using_rexmit_flag = m_bPeerRexmitFlag; UniqueLock rlock(m_RecvLock); m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); // When the drop request was received, it means that there are diff --git a/srtcore/core.h b/srtcore/core.h index 9ee3df6d7..d9f966d94 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -943,13 +943,25 @@ class CUDT /// /// @returns the nmber of packets sent. int sendCtrlAck(CPacket& ctrlpkt, int size); + void sendLossReport(const std::vector< std::pair >& losslist); void processCtrl(const CPacket& ctrlpkt); - void sendLossReport(const std::vector< std::pair >& losslist); - void processCtrlAck(const CPacket& ctrlpkt, const time_point &currtime); + + /// @brief Process incoming control ACK packet. + /// @param ctrlpkt incoming packet + /// @param currtime current clock time + void processCtrlAck(const CPacket& ctrlpkt, const time_point& currtime); + + /// @brief Process incoming control ACKACK packet. + /// @param ctrlpkt incoming packet + /// @param tsArrival time when packet has arrived (used to calculate RTT) + void processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival); + + /// @brief Process incoming loss report (NAK) packet. + /// @param ctrlpkt incoming NAK packet void processCtrlLossReport(const CPacket& ctrlpkt); - /// + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From 7a2b3a22418e050cb06a998f92ffe59162a71d02 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 09:57:00 +0200 Subject: [PATCH 018/683] [core] Refact: moved code to processCtrlHS dedicated function --- srtcore/core.cpp | 222 ++++++++++++++++++++++++----------------------- srtcore/core.h | 8 +- 2 files changed, 118 insertions(+), 112 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 89cf09b16..25fc8abb8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8249,6 +8249,117 @@ void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) leaveCS(m_StatsLock); } +void CUDT::processCtrlHS(const CPacket& ctrlpkt) +{ + CHandShake req; + req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); + + HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); + + if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? + || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION + { + // The peer side has not received the handshake message, so it keeps querying + // resend the handshake packet + + // This condition embraces cases when: + // - this is normal accept() and URQ_INDUCTION was received + // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS + // or CONCLUSION) + // - this is any of URQ_ERROR_* - well... + CHandShake initdata; + initdata.m_iISN = m_iISN; + initdata.m_iMSS = m_config.iMSS; + initdata.m_iFlightFlagSize = m_config.iFlightFlagSize; + + // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. + // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. + initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; + initdata.m_iID = m_SocketID; + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + bool have_hsreq = false; + if (req.m_iVersion > HS_VERSION_UDT4) + { + initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener... + const int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (hs_flags != 0) // has SRT extensions + { + HLOGC(inlog.Debug, + log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) + << " WITH SRT ext"); + have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); + if (!have_hsreq) + { + initdata.m_iVersion = 0; + m_RejectReason = SRT_REJ_ROGUE; + initdata.m_iReqType = URQFailure(m_RejectReason); + } + else + { + // Extensions are added only in case of CONCLUSION (not AGREEMENT). + // Actually what is expected here is that this may either process the + // belated-repeated handshake from a caller (and then it's CONCLUSION, + // and should be added with HSRSP/KMRSP), or it's a belated handshake + // of Rendezvous when it has already considered itself connected. + // Sanity check - according to the rules, there should be no such situation + if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER) + { + LOGC(inlog.Error, + log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " + "handshake phase."); + } + + // The 'extension' flag will be set from this variable; set it to false + // in case when the AGREEMENT response is to be sent. + have_hsreq = initdata.m_iReqType == URQ_CONCLUSION; + HLOGC(inlog.Debug, + log << CONID() << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType) + << " kmdatasize=" << kmdatasize); + } + } + else + { + HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)); + } + } + else + { + initdata.m_iVersion = HS_VERSION_UDT4; + kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX + } + + initdata.m_extension = have_hsreq; + + HLOGC(inlog.Debug, + log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) + << (have_hsreq ? " WITH SRT HS response extensions" : "")); + + CPacket response; + response.setControl(UMSG_HANDSHAKE); + response.allocate(m_iMaxSRTPayloadSize); + + // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. + // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. + if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, + (response), (initdata))) + { + response.m_iID = m_PeerID; + setPacketTS(response, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + if (nbsent) + { + m_tsLastSndTime = steady_clock::now(); + } + } + } + else + { + HLOGC(inlog.Debug, log << CONID() << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED."); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8289,117 +8400,8 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_HANDSHAKE: // 000 - Handshake - { - CHandShake req; - req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); - - HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); - - if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? - || (m_config.bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION - { - // The peer side has not received the handshake message, so it keeps querying - // resend the handshake packet - - // This condition embraces cases when: - // - this is normal accept() and URQ_INDUCTION was received - // - this is rendezvous accept() and there's coming any kind of URQ except AGREEMENT (should be RENDEZVOUS - // or CONCLUSION) - // - this is any of URQ_ERROR_* - well... - CHandShake initdata; - initdata.m_iISN = m_iISN; - initdata.m_iMSS = m_config.iMSS; - initdata.m_iFlightFlagSize = m_config.iFlightFlagSize; - - // For rendezvous we do URQ_WAVEAHAND/URQ_CONCLUSION --> URQ_AGREEMENT. - // For client-server we do URQ_INDUCTION --> URQ_CONCLUSION. - initdata.m_iReqType = (!m_config.bRendezvous) ? URQ_CONCLUSION : URQ_AGREEMENT; - initdata.m_iID = m_SocketID; - - uint32_t kmdata[SRTDATA_MAXSIZE]; - size_t kmdatasize = SRTDATA_MAXSIZE; - bool have_hsreq = false; - if (req.m_iVersion > HS_VERSION_UDT4) - { - initdata.m_iVersion = HS_VERSION_SRT1; // if I remember correctly, this is induction/listener... - int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); - if (hs_flags != 0) // has SRT extensions - { - HLOGC(inlog.Debug, - log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) - << " WITH SRT ext"); - have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); - if (!have_hsreq) - { - initdata.m_iVersion = 0; - m_RejectReason = SRT_REJ_ROGUE; - initdata.m_iReqType = URQFailure(m_RejectReason); - } - else - { - // Extensions are added only in case of CONCLUSION (not AGREEMENT). - // Actually what is expected here is that this may either process the - // belated-repeated handshake from a caller (and then it's CONCLUSION, - // and should be added with HSRSP/KMRSP), or it's a belated handshake - // of Rendezvous when it has already considered itself connected. - // Sanity check - according to the rules, there should be no such situation - if (m_config.bRendezvous && m_SrtHsSide == HSD_RESPONDER) - { - LOGC(inlog.Error, - log << CONID() << "processCtrl/HS: IPE???: RESPONDER should receive all its handshakes in " - "handshake phase."); - } - - // The 'extension' flag will be set from this variable; set it to false - // in case when the AGREEMENT response is to be sent. - have_hsreq = initdata.m_iReqType == URQ_CONCLUSION; - HLOGC(inlog.Debug, - log << CONID() << "processCtrl/HS: processing ok, reqtype=" << RequestTypeStr(initdata.m_iReqType) - << " kmdatasize=" << kmdatasize); - } - } - else - { - HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType)); - } - } - else - { - initdata.m_iVersion = HS_VERSION_UDT4; - kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX - } - - initdata.m_extension = have_hsreq; - - HLOGC(inlog.Debug, - log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) - << (have_hsreq ? " WITH SRT HS response extensions" : "")); - - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); - - // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. - // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. - if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) - { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); - if (nbsent) - { - m_tsLastSndTime = steady_clock::now(); - } - } - } - else - { - HLOGC(inlog.Debug, log << CONID() << "processCtrl: ... not INDUCTION, not ERROR, not rendezvous - IGNORED."); - } - + processCtrlHS(ctrlpkt); break; - } case UMSG_SHUTDOWN: // 101 - Shutdown m_bShutdown = true; diff --git a/srtcore/core.h b/srtcore/core.h index d9f966d94..7da1c8f36 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -948,12 +948,12 @@ class CUDT void processCtrl(const CPacket& ctrlpkt); /// @brief Process incoming control ACK packet. - /// @param ctrlpkt incoming packet + /// @param ctrlpkt incoming ACK packet /// @param currtime current clock time void processCtrlAck(const CPacket& ctrlpkt, const time_point& currtime); /// @brief Process incoming control ACKACK packet. - /// @param ctrlpkt incoming packet + /// @param ctrlpkt incoming ACKACK packet /// @param tsArrival time when packet has arrived (used to calculate RTT) void processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival); @@ -961,6 +961,10 @@ class CUDT /// @param ctrlpkt incoming NAK packet void processCtrlLossReport(const CPacket& ctrlpkt); + /// @brief Process incoming handshake control packet + /// @param ctrlpkt incoming HS packet + void processCtrlHS(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From eb6e0296bc019d82754c33262f763bd4696ec6eb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:01:14 +0200 Subject: [PATCH 019/683] [core] Refact: moved code to processCtrlDropReq dedicated function --- srtcore/core.cpp | 78 +++++++++++++++++++++++++----------------------- srtcore/core.h | 4 +++ 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 25fc8abb8..1c7019248 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8360,6 +8360,45 @@ void CUDT::processCtrlHS(const CPacket& ctrlpkt) } } +void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) +{ + { + const bool using_rexmit_flag = m_bPeerRexmitFlag; + UniqueLock rlock(m_RecvLock); + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); + // When the drop request was received, it means that there are + // packets for which there will never be ACK sent; if the TSBPD thread + // is currently in the ACK-waiting state, it may never exit. + if (m_bTsbPd) + { + HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); + CSync cc(m_RcvTsbPdCond, rlock); + cc.signal_locked(rlock); + } + } + + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; + + dropFromLossLists(dropdata[0], dropdata[1]); + + // move forward with current recv seq no. + // SYMBOLIC: + // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo + // && dropdata[1] >% m_iRcvCurrSeqNo ) + if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) + && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) + { + HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" + << dropdata[0] << "-" << dropdata[1] << " <-- set as current seq"); + m_iRcvCurrSeqNo = dropdata[1]; + } + else + { + HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" + << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8416,44 +8455,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_DROPREQ: // 111 - Msg drop request - { - const bool using_rexmit_flag = m_bPeerRexmitFlag; - UniqueLock rlock(m_RecvLock); - m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - CSync cc(m_RcvTsbPdCond, rlock); - cc.signal_locked(rlock); - } - } - - { - int32_t* dropdata = (int32_t*)ctrlpkt.m_pcData; - - dropFromLossLists(dropdata[0], dropdata[1]); - - // move forward with current recv seq no. - // SYMBOLIC: - // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo - // && dropdata[1] >% m_iRcvCurrSeqNo ) - if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) - && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) - { - HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " <-- set as current seq"); - m_iRcvCurrSeqNo = dropdata[1]; - } - else - { - HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); - } - } - + processCtrlDropReq(ctrlpkt); break; case UMSG_PEERERROR: // 1000 - An error has happened to the peer side diff --git a/srtcore/core.h b/srtcore/core.h index 7da1c8f36..ae847032a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -965,6 +965,10 @@ class CUDT /// @param ctrlpkt incoming HS packet void processCtrlHS(const CPacket& ctrlpkt); + /// @brief Process incoming drop request control packet + /// @param ctrlpkt incoming drop request packet + void processCtrlDropReq(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From efd0c0b24ce7ae3e2ceceec3c371a71a3a35f365 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:03:19 +0200 Subject: [PATCH 020/683] [core] Refact: moved code to processCtrlShutdown dedicated function --- srtcore/core.cpp | 23 ++++++++++++++--------- srtcore/core.h | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1c7019248..98b9c4deb 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8399,6 +8399,19 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } +void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) +{ + m_bShutdown = true; + m_bClosing = true; + m_bBroken = true; + m_iBrokenCounter = 60; + + // This does the same as it would happen on connection timeout, + // just we know about this state prematurely thanks to this message. + updateBrokenConnection(); + completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8443,15 +8456,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - m_bShutdown = true; - m_bClosing = true; - m_bBroken = true; - m_iBrokenCounter = 60; - - // This does the same as it would happen on connection timeout, - // just we know about this state prematurely thanks to this message. - updateBrokenConnection(); - completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! + processCtrlShutdown(ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request diff --git a/srtcore/core.h b/srtcore/core.h index ae847032a..f0bec9dce 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -969,6 +969,10 @@ class CUDT /// @param ctrlpkt incoming drop request packet void processCtrlDropReq(const CPacket& ctrlpkt); + /// @brief Process incoming shutdown control packet + /// @param ctrlpkt incoming shutdown packet + void processCtrlShutdown(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From d5f1d08132136224bff66b9b68024df350a36547 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 10:05:42 +0200 Subject: [PATCH 021/683] [core] Refact: moved code to processCtrlUserDefined dedicated function --- srtcore/core.cpp | 59 +++++++++++++++++++++++++----------------------- srtcore/core.h | 4 ++++ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 98b9c4deb..381669b30 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8412,6 +8412,36 @@ void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! } +void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) +{ + HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) + << ", value=" << ctrlpkt.getExtendedType()); + + // This has currently two roles in SRT: + // - HSv4 (legacy) handshake + // - refreshed KMX (initial KMX is done still in the HS process in HSv5) + const bool understood = processSrtMsg(&ctrlpkt); + // CAREFUL HERE! This only means that this update comes from the UMSG_EXT + // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean + // the handshake version, but the reason of calling this function. + // + // Fortunately, the only messages taken into account in this function + // are HSREQ and HSRSP, which should *never* be interchanged when both + // parties are HSv5. + if (understood) + { + if (ctrlpkt.getExtendedType() == SRT_CMD_HSREQ || ctrlpkt.getExtendedType() == SRT_CMD_HSRSP) + { + updateAfterSrtHandshake(HS_VERSION_UDT4); + } + } + else + { + updateCC(TEV_CUSTOM, EventVariant(&ctrlpkt)); + } +} + void CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. @@ -8469,39 +8499,12 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) // currently only this error is signalled from the peer side // if recvfile() failes (e.g., due to disk fail), blcoked sendfile/send should return immediately // giving the app a chance to fix the issue - m_bPeerHealth = false; break; case UMSG_EXT: // 0x7FFF - reserved and user defined messages - HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) - << ", value=" << ctrlpkt.getExtendedType()); - { - // This has currently two roles in SRT: - // - HSv4 (legacy) handshake - // - refreshed KMX (initial KMX is done still in the HS process in HSv5) - bool understood = processSrtMsg(&ctrlpkt); - // CAREFUL HERE! This only means that this update comes from the UMSG_EXT - // message received, REGARDLESS OF WHAT IT IS. This version doesn't mean - // the handshake version, but the reason of calling this function. - // - // Fortunately, the only messages taken into account in this function - // are HSREQ and HSRSP, which should *never* be interchanged when both - // parties are HSv5. - if (understood) - { - if (ctrlpkt.getExtendedType() == SRT_CMD_HSREQ || ctrlpkt.getExtendedType() == SRT_CMD_HSRSP) - { - updateAfterSrtHandshake(HS_VERSION_UDT4); - } - } - else - { - updateCC(TEV_CUSTOM, EventVariant(&ctrlpkt)); - } - } + processCtrlUserDefined(ctrlpkt); break; default: diff --git a/srtcore/core.h b/srtcore/core.h index f0bec9dce..6be1a9011 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -973,6 +973,10 @@ class CUDT /// @param ctrlpkt incoming shutdown packet void processCtrlShutdown(const CPacket& ctrlpkt); + /// @brief Process incoming user defined control packet + /// @param ctrlpkt incoming user defined packet + void processCtrlUserDefined(const CPacket& ctrlpkt); + /// @brief Update sender's loss list on an incoming acknowledgement. /// @param ackdata_seqno sequence number of a data packet being acknowledged void updateSndLossListOnACK(int32_t ackdata_seqno); From f1f2dd4d7699d978ec7815e84713d2beb51595ad Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:04:02 +0200 Subject: [PATCH 022/683] [docs] Reorganized and cleaned up the docs folder (#1911) --- CONTRIBUTING.md | 2 +- README.md | 12 +-- docs/API/API-functions.md | 6 +- docs/API/API-socket-options.md | 8 +- docs/API/API.md | 6 +- docs/API/statistics.md | 22 +++--- docs/README.md | 69 ++++++++++++++++-- docs/{ => apps}/srt-live-transmit.md | 0 .../srt-multiplex.md} | 0 docs/{ => apps}/srt-tunnel.md | 0 .../Compiling.md => build/build-android.md} | 26 +++++-- docs/{build_iOS.md => build/build-iOS.md} | 0 .../build-options.md} | 6 +- docs/{ => build}/build-win.md | 2 +- .../developers-guide.md} | 12 +-- .../low-level-info.md} | 0 .../making-srt-better.md} | 0 .../access-control.md} | 6 +- docs/{ => features}/bonding-intro.md | 2 +- docs/{ => features}/encryption.md | 38 ++++++---- docs/{ => features}/handshake.md | 10 +-- .../images/block-aligned-5rx10c.png | Bin docs/{ => features}/images/block-aligned.png | Bin ...n-block-aligned-5rx10c-deleted-packets.png | Bin .../images/non-block-aligned-5rx10c.png | Bin .../images/non-block-aligned.png | Bin .../images/packet-filter-mechanism.png | Bin .../images/rebuild-missing-sequence.png | Bin .../images/srt-encryption-1.png | Bin .../images/srt-encryption-2.png | Bin .../images/staircase-pattern-5rx10c.png | Bin docs/{ => features}/live-streaming.md | 17 ++--- .../packet-filtering-and-fec.md | 16 ++-- docs/{ => features}/socket-groups.md | 0 docs/gstreamer.md | 62 ---------------- .../images/srt-history-good-signal.png} | Bin .../images/srt-transmission-bad-signal.png} | Bin docs/{ => misc}/why-srt-was-created.md | 4 +- scripts/build-android/README.md | 5 ++ {docs/Android => scripts/build-android}/mkall | 0 {docs/Android => scripts/build-android}/mksrt | 0 {docs/Android => scripts/build-android}/mkssl | 0 .../Android => scripts/build-android}/packjni | 0 .../build-android}/prepare_build | 0 srtcore/api.h | 2 +- 45 files changed, 174 insertions(+), 159 deletions(-) rename docs/{ => apps}/srt-live-transmit.md (100%) rename docs/{SRT-Multiplex.md => apps/srt-multiplex.md} (100%) rename docs/{ => apps}/srt-tunnel.md (100%) rename docs/{Android/Compiling.md => build/build-android.md} (76%) rename docs/{build_iOS.md => build/build-iOS.md} (100%) rename docs/{BuildOptions.md => build/build-options.md} (98%) rename docs/{ => build}/build-win.md (99%) rename docs/{DevelopersGuide.md => dev/developers-guide.md} (94%) rename docs/{LowLevelInfo.md => dev/low-level-info.md} (100%) rename docs/{reporting.md => dev/making-srt-better.md} (100%) rename docs/{AccessControl.md => features/access-control.md} (99%) rename docs/{ => features}/bonding-intro.md (99%) rename docs/{ => features}/encryption.md (95%) rename docs/{ => features}/handshake.md (99%) rename docs/{ => features}/images/block-aligned-5rx10c.png (100%) rename docs/{ => features}/images/block-aligned.png (100%) rename docs/{ => features}/images/non-block-aligned-5rx10c-deleted-packets.png (100%) rename docs/{ => features}/images/non-block-aligned-5rx10c.png (100%) rename docs/{ => features}/images/non-block-aligned.png (100%) rename docs/{ => features}/images/packet-filter-mechanism.png (100%) rename docs/{ => features}/images/rebuild-missing-sequence.png (100%) rename docs/{ => features}/images/srt-encryption-1.png (100%) rename docs/{ => features}/images/srt-encryption-2.png (100%) rename docs/{ => features}/images/staircase-pattern-5rx10c.png (100%) rename docs/{ => features}/live-streaming.md (95%) rename docs/{ => features}/packet-filtering-and-fec.md (98%) rename docs/{ => features}/socket-groups.md (100%) delete mode 100644 docs/gstreamer.md rename docs/{images/SRT_History_Good_Signal.png => misc/images/srt-history-good-signal.png} (100%) rename docs/{images/SRT_Transmission_Bad_Signal.png => misc/images/srt-transmission-bad-signal.png} (100%) rename docs/{ => misc}/why-srt-was-created.md (97%) create mode 100644 scripts/build-android/README.md rename {docs/Android => scripts/build-android}/mkall (100%) rename {docs/Android => scripts/build-android}/mksrt (100%) rename {docs/Android => scripts/build-android}/mkssl (100%) rename {docs/Android => scripts/build-android}/packjni (100%) rename {docs/Android => scripts/build-android}/prepare_build (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6f226602..239e25a2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Submit a pull request at any time, whether an issue has been created or not. It * SRT protocol definitions * portability and platform-specific parts -Please follow the [Developer's guide](./docs/DevelopersGuide.md). +Please follow the [SRT Developer's Guide](docs/dev/developers-guide.md). ## Code Style diff --git a/README.md b/README.md index eda49484e..1c9aff2d8 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,14 @@ As audio/video packets are streamed from a source to a destination device, SRT d ### Guides * [SRT API Documents](docs/API/) -* [Using the `srt-live-transmit` App](docs/srt-live-transmit.md) -* [Developer's Guide](docs/DevelopersGuide.md) +* [Using the `srt-live-transmit` App](docs/apps/srt-live-transmit.md) +* [SRT Developer's Guide](docs/dev/developers-guide.md) * [Contributing](CONTRIBUTING.md) -* [Reporting problems](docs/reporting.md) +* [Reporting Issues](docs/dev/making-srt-better.md) * SRT RFC: [Latest IETF Draft](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00), [Latest Working Copy](https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html), [GitHub Repo](https://github.com/Haivision/srt-rfc) * SRT CookBook: [Website](https://srtlab.github.io/srt-cookbook), [GitHub Repo](https://github.com/SRTLab/srt-cookbook) * [SRT Protocol Technical Overview](https://github.com/Haivision/srt/files/2489142/SRT_Protocol_TechnicalOverview_DRAFT_2018-10-17.pdf) -* [Why SRT Was Created](docs/why-srt-was-created.md) +* [Why SRT Was Created](docs/misc/why-srt-was-created.md) ## Requirements @@ -50,7 +50,7 @@ As audio/video packets are streamed from a source to a destination device, SRT d * OpenSSL * Pthreads (for POSIX systems it's builtin, for Windows there's a library) -For detailed description of the build system and options, please read [BuildOptions.md](docs/BuildOptions.md). +For detailed description of the build system and options, please read [SRT Build Options](docs/build/build-options.md). ### Build on Linux @@ -127,7 +127,7 @@ make ### Build on Windows -Follow the [Windows build instructions](docs/build-win.md). +Follow the [Building SRT for Windows](docs/build/build-win.md) instructions. [appveyor-badge]: https://img.shields.io/appveyor/ci/Haivision/srt/master.svg?label=Windows [appveyor]: https://ci.appveyor.com/project/Haivision/srt diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 734ce6221..0e65d5ab3 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1507,7 +1507,7 @@ The following options are allowed to be set on the member socket: * [srt_setsockopt, srt_setsockflag](#srt_setsockopt-srt_setsockflag) * [srt_getversion](#srt_getversion) -**NOTE**: For more information, see [Getting and Setting Options](API-socket-options.md#getting-and-setting-options). +**NOTE**: For more information, see [SRT API Socket Options, Getting and Setting Options](API-socket-options.md#getting-and-setting-options). ### srt_getpeername ``` @@ -1732,8 +1732,8 @@ call [`srt_sendmsg2`](#srt_sendmsg) or [`srt_recvmsg2`](#srt_recvmsg2) function for a group, you should pass an array here so that you can retrieve the status of particular member sockets. If you pass an array that is too small, your `grpdata_size` field will be rewritten with the current number of members, but without filling in -the array. For details, see the (Bonding introduction)[../bonding-intro.md] and -(Socket Groups)[../socket-groups.md] documents. +the array. For details, see the [SRT Connection Bonding](../features/bonding-intro.md) and +[SRT Socket Groups](../features/socket-groups.md) documents. **Helpers for [`SRT_MSGCTRL`](#SRT_MSGCTRL):** diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index e8e391ee9..2680e060f 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -796,7 +796,7 @@ complete (not all packets received or there was a packet loss) it will not be copied to the application's buffer. Messages that are sent later, but were earlier reassembled by the receiver, will be delivered once ready, if the `inorder` flag was set to false. -See [`srt_sendmsg`](API.md#sending-and-receiving)). +See [`srt_sendmsg`](API.md#sending-and-receiving). As a comparison to the standard system protocols, the Stream API does transmission similar to TCP, whereas the Message API functions like the @@ -920,7 +920,7 @@ The connection will be rejected with `SRT_REJ_FILTER` code in the following case In case of the built-in `fec` filter, the mandatory parameter is `cols`, all others have their default values. For example, the configuration specified as `fec,cols:10` is `fec,cols:10,rows:1,arq:onreq,layout:even`. See how to -[configure the FEC Filter](packet-filtering-and-fec.md#configuring-the-fec-filter). +configure the FEC filter in [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md#configuring-the-fec-filter). Below in the table are examples for the built-in `fec` filter. Note that the negotiated config need not have parameters in the given order. @@ -950,7 +950,7 @@ Reading this option after the connection is established will return the full configuration that has been agreed upon by both parties (including default values). -For details, see [Packet Filtering & FEC](packet-filtering-and-fec.md). +For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). [Return to list](#list-of-options) @@ -1455,7 +1455,7 @@ will be able to retrieve this stream ID from the socket that is returned from `srt_accept` (for a connected socket with that stream ID). You usually use SET on the socket used for `srt_connect`, and GET on the socket retrieved from `srt_accept`. This string can be used completely free-form. However, it's highly -recommended to follow the [SRT Access Control guidlines](../AccessControl.md). +recommended to follow the [SRT Access Control (Stream ID) Guidlines](../features/access-control.md). - As this uses internally the `std::string` type, there are additional functions for it in the legacy/C++ API (udt.h): `srt::setstreamid` and `srt::getstreamid`. diff --git a/docs/API/API.md b/docs/API/API.md index f1d3a1209..594f9a12d 100644 --- a/docs/API/API.md +++ b/docs/API/API.md @@ -281,7 +281,7 @@ nb = srt_recvmsg2(u, buf, nb, &mc); ### Transmission Types Available in SRT Mode settings determine how the sender and receiver functions work. The main -[socket options](API-socket-options.md) that control it are: +SRT [socket options](API-socket-options.md) that control it are: - `SRTO_TRANSTYPE`. Sets several parameters in accordance with the selected mode: @@ -519,7 +519,7 @@ either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. ### Transmission Method: Live -Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [parameters](API-socket-options.md): +Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [socket options](API-socket-options.md): - `SRTO_TSBPDMODE` = true - `SRTO_RCVLATENCY` = 120 @@ -596,7 +596,7 @@ NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. ### Transmission Method: Buffer -Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [parameters](API-socket-options.md): +Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [socket options](API-socket-options.md): - `SRTO_TSBPDMODE` = false - `SRTO_RCVLATENCY` = 0 diff --git a/docs/API/statistics.md b/docs/API/statistics.md index f82b8074c..156224fd0 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -246,7 +246,7 @@ The total number of packets that failed to be decrypted at the receiver side. Av #### pktSndFilterExtraTotal -The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for sender. +The total number of packet filter control packets generated by the packet filter (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for sender. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. @@ -254,7 +254,7 @@ If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket O #### pktRcvFilterExtraTotal -The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of packet filter control packets received by the packet filter (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. Packet filter control packets contain only control information necessary for the packet filter. The type of these packets is DATA. @@ -262,13 +262,13 @@ If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket O #### pktRcvFilterSupplyTotal -The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets recovered by the packet filter at the receiver side (e.g., FEC rebuilt packets; refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. #### pktRcvFilterLossTotal -The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md)). Available for receiver. +The total number of lost DATA packets **not** recovered by the packet filter at the receiver side (refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md)). Available for receiver. If the `SRTO_PACKETFILTER` socket option is disabled (refer to [SRT API Socket Options](API-socket-options.md)), this statistic is equal to 0. Introduced in SRT v1.4.0. @@ -363,25 +363,25 @@ Same as [pktRecvNAKTotal](#pktRecvNAKTotal), but for a specified interval. Same as [pktSndFilterExtraTotal](#pktSndFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterExtra Same as [pktRcvFilterExtraTotal](#pktRcvFilterExtraTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterSupply Same as [pktRcvFilterSupplyTotal](#pktRcvFilterSupplyTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### pktRcvFilterLoss Same as [pktRcvFilterLossTotal](#pktRcvFilterLossTotal), but for a specified interval. -Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../packet-filtering-and-fec.md). +Introduced in v1.4.0. Refer to [SRT Packet Filtering & FEC](../features/packet-filtering-and-fec.md). #### mbpsSendRate @@ -727,12 +727,12 @@ that is received late. SRT group statistics are implemented for SRT Connection Bonding feature and available since SRT v1.5.0. Check the following documentation and code examples for details: -- [Introduction in SRT Connection Bonding feature](../bonding-intro.md), -- [The concept of socket groups](../socket-groups.md). Here you will also find the information regarding `srt-test-live` application for testing Connection Bonding, +- Introduction in [SRT Connection Bonding](../features/bonding-intro.md), +- The concept of [SRT Socket Groups](../features/socket-groups.md). Here you will also find the information regarding the `srt-test-live` application for testing Connection Bonding, - Check also [SRT API](API.md) and [SRT API Functions](API-functions.md) documentation for Connection Bonding related updates, - Code examples: simple [client](https://github.com/Haivision/srt/blob/master/examples/test-c-client-bonding.c) and [server](https://github.com/Haivision/srt/blob/master/examples/test-c-server-bonding.c) implementation. -`srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [API functions](API-functions.md) for usage instructions. +`srt_bistats(SRTSOCKET u, ...)` function can be used with a socket group ID as a first argument to get statistics for a group. Most values of the `SRT_TRACEBSTATS` will be filled with zeros except for the fields listed in [Summary Table](#group-summary-table) below. Refer to the documentation of the [SRT API Functions](API-functions.md) for usage instructions. ### Summary Table diff --git a/docs/README.md b/docs/README.md index ed2ad475c..2d91117d7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,67 @@ # Documentation Overview + + ## SRT API Documents -| Folder Name | File Name | Description | Refer as | -| :---------: | ------------------------------------------------------ | --------------------------------------------------- | ---------------------- | -| API | [API.md](API/API.md) | Detailed description of the SRT C API | SRT API | -| API | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions | SRT API Functions | -| API | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API | SRT API Socket Options | -| API | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics | SRT Statistics | +| Document Title | Folder | File Name | Description | +| :-------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | +| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | +| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | +| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | +| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | +| | | | | + +## Build Instructions + +| Document Title | Folder | File Name | Description | +| :------------------------------------------------- | :---------------------------- | :----------------------------------------- | :----------------------------------------------------------- | +| [Building SRT for Android](build/build-android.md) | [build](build/) | [build-android.md](build/build-android.md) | SRT build instructions for Android. | +| [Building SRT for iOS](build/build-iOS.md) | [build](build/) | [build-iOS.md](build/build-iOS.md) | SRT build instructions for iOS. | +| [SRT Build Options](build/build-options.md) | [build](build/) | [build-options.md](build/build-options.md) | Description of CMake build system, configure script, and
build options. | +| [Building SRT for Windows](build/build-win.md) | [build](build/) | [build-win.md](build/build-win.md) | SRT build instructions for Windows. | +| | | | | + +## Development Documents + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------- | :---------------------------- | :----------------------------------------------- | :----------------------------------------------------------- | +| [SRT Developer's Guide](dev/developers-guide.md) | [dev](dev/) | [developers-guide.md](dev/developers-guide.md) | Development setup, project structure, coding rules,
submitting issues & PRs, etc. | +| [Low Level Info](dev/low-level-info.md) | [dev](dev/) | [low-level-info.md](dev/low-level-info.md) | Low level information for the SRT project (only
mutex locking). | +| [Making SRT Better](dev/making-srt-better.md) | [dev](dev/) | [making-srt-better.md](dev/making-srt-better.md) | Guidelines for problem reporting, collecting debug logs
and pcaps. | +| | | | | + +## Features + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------------- | :---------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| [SRT Access Control](features/access-control.md)
[(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | +| [SRT Connection Bonding](features/bonding-intro.md) | [features](features/) | [bonding-intro.md](features/bonding-intro.md) | Introduction to Connection Bonding. Description
of group (bonded) connections. | +| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | +| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | +| [Live Streaming](features/live-streaming.md)
[Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| [SRT Packet](features/packet-filtering-and-fec.md)
[Filtering & FEC](features/packet-filtering-and-fec.md) | [features](features/) | [packet-filtering-and-fec.md](features/packet-filtering-and-fec.md) | Description of SRT packet filtering mechanism,
including FEC. | +| [SRT Socket Groups](features/socket-groups.md) | [features](features/) | [socket-groups.md](features/socket-groups.md) | Description of socket groups in SRT (Connection
Bonding). Here you will also find the information
regarding the `srt-test-live` application for testing
Connection Bonding. | +| | | | | + +## Sample Applications + +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | +| [Using the](apps/srt-live-transmit.md)
[`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the](apps/srt-multiplex.md)
[`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | +| [Using the](apps/srt-tunnel.md)
[`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| | | | | + +## Miscellaneous + +| Document Title | Folder | File Name | Description | +| :------------------------------------------------- | :---------------------------- | :---------------------------------------------------- | :----------------------------------------------------------- | +| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| | | | | + diff --git a/docs/srt-live-transmit.md b/docs/apps/srt-live-transmit.md similarity index 100% rename from docs/srt-live-transmit.md rename to docs/apps/srt-live-transmit.md diff --git a/docs/SRT-Multiplex.md b/docs/apps/srt-multiplex.md similarity index 100% rename from docs/SRT-Multiplex.md rename to docs/apps/srt-multiplex.md diff --git a/docs/srt-tunnel.md b/docs/apps/srt-tunnel.md similarity index 100% rename from docs/srt-tunnel.md rename to docs/apps/srt-tunnel.md diff --git a/docs/Android/Compiling.md b/docs/build/build-android.md similarity index 76% rename from docs/Android/Compiling.md rename to docs/build/build-android.md index fbfe8893c..f2dec839e 100644 --- a/docs/Android/Compiling.md +++ b/docs/build/build-android.md @@ -1,5 +1,11 @@ -# Establishing a Build Environment -## Installing the Android NDK +# Building SRT for Android + +**NOTE:** The scripts have been moved to [scripts/build-android](../../scripts/build-android/) folder. + +## Establishing a Build Environment + +### Installing the Android NDK + The Android NDK is required to build native modules for Android. Consider installing the latest version of cmake. The higher version of cmake the better. As of writing the current version of CMake is 3.18.4 You can download Cmake from the following website: @@ -8,11 +14,19 @@ You can download Cmake from the following website: Download the NDK r19 or newer archive from the following site: [Download the Android NDK on developer.android.com](https://developer.android.com/ndk/downloads/index.html) To install the Android NDK, simply expand the archive in the folder where you want to install it. -## OpenSSL + +### OpenSSL + Google removed openssl from Android 7+. You must build openssl libs by yourself. -# Configure the NDK path + +## Configure the NDK Path + Edit the ```mkall``` script to configure NDK path. Set the ```NDK``` to the directory where the NDK is installed. -# Build SRT for Android + +## Build SRT for Android + Run ```/bin/bash mkall > build.log``` script. Libraries will be installed to ```./target-architecture/lib```. -# Export SRT libraries + +## Export SRT Libraries + Run ```/bin/bash packjni``` to generate ```jniLibs``` archive for Android Studio. diff --git a/docs/build_iOS.md b/docs/build/build-iOS.md similarity index 100% rename from docs/build_iOS.md rename to docs/build/build-iOS.md diff --git a/docs/BuildOptions.md b/docs/build/build-options.md similarity index 98% rename from docs/BuildOptions.md rename to docs/build/build-options.md index adf718031..805214338 100644 --- a/docs/BuildOptions.md +++ b/docs/build/build-options.md @@ -10,10 +10,10 @@ interpreter) that can make operating with the options easier. The `cmake` build system was tested on the following platforms: - Linux (various flavors) - - macOS (see this [separate document](build_iOS.md)) + - macOS (see [Building SRT for iOS](build-iOS.md)) - Windows with MinGW - - Windows with Microsoft Visual Studio (see this [separate document](build-win.md)) - - Android (see this [separate document](Android/Compiling.md)) + - Windows with Microsoft Visual Studio (see [Building SRT for Windows](build-win.md)) + - Android (see [Building SRT for Android](build-android.md)) - Cygwin (only for testing) The `configure` script wasn't tested on Windows (other than on Cygwin). diff --git a/docs/build-win.md b/docs/build/build-win.md similarity index 99% rename from docs/build-win.md rename to docs/build/build-win.md index f3c3c1b3c..3f47b310b 100644 --- a/docs/build-win.md +++ b/docs/build/build-win.md @@ -1,4 +1,4 @@ -# SRT Build Instructions +# Building SRT for Windows diff --git a/docs/DevelopersGuide.md b/docs/dev/developers-guide.md similarity index 94% rename from docs/DevelopersGuide.md rename to docs/dev/developers-guide.md index cc021cb6c..3084373e1 100644 --- a/docs/DevelopersGuide.md +++ b/docs/dev/developers-guide.md @@ -66,21 +66,21 @@ cmake .. -DENABLE_UNITTESTS=ON cmake --build ./ ``` -**Note.** If you're using Windows, please refer to [Windows Build Instructions](./build-win.md). +**Note.** If you are using Windows, please refer to [Building SRT for Windows](../build/build-win.md) instructions. -**Note.** Please see the following document for the build options: [BuildOptions.md](./BuildOptions.md). +**Note.** Please see the following document for the build options: [SRT Build Options](../build/build-options.md). To see the full list of make options run `cmake .. -LAH` from the `_build` folder. **Note.** There is an alternative `configure` script provided. It is **NOT** an alternative Autotools build, but a convenience script. It processes the usual format of `--long-options` and calls `cmake` with appropriate options in the end. This script is dependent on "tcl" package. -Please see the following document for `configure` usage: [BuildOptions.md](./BuildOptions.md). +Please see the following document for `configure` usage: [SRT Build Options](../build/build-options.md). The build output is in the `_build` directory. The following applications can be found there. * `srt-live-transmit` - A sample application to transmit a live stream from source medium (UDP/SRT/`stdin`) -to the target medium (UDP/SRT/`stdout`). See [srt-live-transmit.md](./srt-live-transmit.md) for more info. +to the target medium (UDP/SRT/`stdout`). See [Using the `srt-live-transmit` App](../apps/srt-live-transmit.md) for more info. * `srt-file-transmit` - A sample application to transmit files with SRT. -* `srt-tunnel` - A sample application to set up an SRT tunnel for TCP traffic. See [srt-tunnel.md](./srt-tunnel.md) for more info. +* `srt-tunnel` - A sample application to set up an SRT tunnel for TCP traffic. See [Using the `srt-tunnel` App](../apps/srt-tunnel.md) for more info. * `tests-srt` - unit testing application. ## Language standard requirements @@ -111,7 +111,7 @@ for example). The SRT installation has the following folders: -* apps - the folder contains [srt-live-transmit](./srt-live-transmit.md), `srt-file-transmit` and `srt-tunnel` sample applications. +* apps - the folder contains [srt-live-transmit](../apps/srt-live-transmit.md), `srt-file-transmit` and [srt-tunnel](../apps/srt-tunnel.md) sample applications. * *common - holds some platform-dependent code. * *docs - contains all the documentation in the GitHub Markdown format. * *examples - example applications (use `-DENABLE_EXAMPLES=ON` CMake build option to include in the build) diff --git a/docs/LowLevelInfo.md b/docs/dev/low-level-info.md similarity index 100% rename from docs/LowLevelInfo.md rename to docs/dev/low-level-info.md diff --git a/docs/reporting.md b/docs/dev/making-srt-better.md similarity index 100% rename from docs/reporting.md rename to docs/dev/making-srt-better.md diff --git a/docs/AccessControl.md b/docs/features/access-control.md similarity index 99% rename from docs/AccessControl.md rename to docs/features/access-control.md index 402f90624..101502566 100644 --- a/docs/AccessControl.md +++ b/docs/features/access-control.md @@ -1,4 +1,4 @@ -# SRT Access Control Guidelines +# SRT Access Control (Stream ID) Guidelines ## Motivation @@ -6,7 +6,7 @@ One type of information that can be interchanged when a connection is being established in SRT is "Stream ID", which can be used in a caller-listener connection layout. This is a string of maximum 512 characters set on the caller side. It can be retrieved at the listener side on the newly accepted socket -through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](API/API-socket-options.md)). +through a socket option (see `SRTO_STREAMID` in [SRT API Socket Options](../API/API-socket-options.md)). As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, @@ -121,7 +121,7 @@ to the caller. This specifies that the file is expected to be transmitted from the caller to the listener and its name is `results.csv`. -### Rejection codes +### Rejection Codes The listener callback handler is also able to decide about rejecting the incoming connection. In a normal situation, the rejection code is predefined diff --git a/docs/bonding-intro.md b/docs/features/bonding-intro.md similarity index 99% rename from docs/bonding-intro.md rename to docs/features/bonding-intro.md index 578d644cd..e8c747e5c 100644 --- a/docs/bonding-intro.md +++ b/docs/features/bonding-intro.md @@ -21,7 +21,7 @@ How the links are utilized within a group depends on the group type. The simplest type, broadcast, utilizes all links at once to send the same data. To learn more about socket groups and their abilities, please read the -[detailed document](socket-groups.md). +[SRT Socket Groups](socket-groups.md) document. # Reminder: Using sockets for establishing a connection diff --git a/docs/encryption.md b/docs/features/encryption.md similarity index 95% rename from docs/encryption.md rename to docs/features/encryption.md index 614a11303..60e38dc15 100644 --- a/docs/encryption.md +++ b/docs/features/encryption.md @@ -1,7 +1,11 @@ -# Introduction +# SRT Encryption + +**NOTE:** This document might be outdated, please consult [Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. + This document describes an encryption mechanism that protects the payload of SRT streams. Despite using standard cryptographic algorithms, the mechanism is unique and does not interoperate with any known third party stream encryption method. -### Terminology +## Terminology + | Term | Description | |------|-------------| | AEAD | Authenticated Encryption with Associated Data | @@ -53,7 +57,8 @@ This document describes an encryption mechanism that protects the payload of SRT | TU | Transmission Unit | | UDP | User Datagram Protocol | -### References +## References + * [ANSX9.102] Accredited Standards Committee, Wrapping of Keys and Associated Data, ANS X9.102, not for free document. * [FIPS 140-2] Security Requirements for Cryptographic Modules, NIST, [FIPS PUB 140-2](https://csrc.nist.gov/csrc/media/publications/fips/140/2/final/documents/fips1402.pdf), May 2001. * [FIPS 140-3] Security Requirements for Cryptographic Modules, NIST, [FIPS PUB 140-3](https://csrc.nist.gov/publications/detail/fips/140/3/archive/2009-12-11), December 2009. @@ -71,7 +76,8 @@ This document describes an encryption mechanism that protects the payload of SRT * [RFC6070] PBKDF2 Test Vectors * [SRTP-EKT] Encrypted Key Transport for Secure RTP, D. McGrew, F. Andreasen, D. Wing, K. Fisher, [draft-ietf-avt-srtp-ekt-02](https://tools.ietf.org/html/draft-ietf-avt-srtp-ekt-02), March 2011. -### Operators +## Operators + | Operator | Setting | |----------|---------| | a \|\| b | Concatenation | @@ -84,7 +90,8 @@ This document describes an encryption mechanism that protects the payload of SRT | PRNG(n) | Pseudo Random Number Generator (n bits) | | PBKDF2(p,s,i,l) | Password-based Key Derivation Function (PKCS #5)
p: password, s: salt, i: iterations, l: key length | -# Overview +## Overview + AES in counter mode (AES-CTR) is used with a short lived key to encrypt the media stream. This cipher is suitable for random access of a continuous stream, content protection (used by HDCP 2.0), and strong confidentiality when the counter is managed properly. The short lived key is randomly generated by the sender and transmitted within the stream (KM Tx Period), wrapped with another longer-term key, the Key Encrypting Key (KEK). For connection-oriented transport such as SRT, there is no need to periodically transmit the short lived key since no party can join the stream at any time. @@ -95,48 +102,49 @@ A pre-shared password used with a password-based key derivation mechanism is pro The short lived key, hereafter called the Stream Encrypting Key (SEK), is regenerated for cryptographic reasons when enough packets have been encrypted with it (KM Refresh Rate). To ensure seamless rekeying, the next key to use is transmitted in advance to receivers (KM Pre-Announce) so they can switch keys without disruption when rekeying occurs. KM Refresh Rate and KM Pre-Announce are system parameters that can be configurable options if shorter time than the cryptographic limit is required (for example to limit the material obtained from a compromised SEK). -## Definitions +### Definitions + This section defines the elements of the SRT encryption mechanism. Figure 1 shows the encryption of arbitrary SRT payload. ![Figure 1][figure1] Figure 1 -### Ciphers (AES-CTR) +#### Ciphers (AES-CTR) The payload is encrypted with a cipher in counter mode (AES-CTR). The counter mode is one of the only cipher mode suitable for continuous stream encryption that permits decryption from any point, without access to start of the stream (random access), and for the same reason tolerates packet lost. The Electronic Code Book (ECB) mode also has these characteristics but does not provide serious confidentiality and is not recommended in cryptography. -### Media Stream message (MSmsg) +#### Media Stream message (MSmsg) The Media Stream message is formed from the SRT media stream (data) packets with some elements of the SRT header used for the cryptography. SRT header already carries a 32-bit packet sequence that is used for the cipher’s counter (ctr) and 2 bits are stolen from the header’s message number (then reduced to 27-bits) for the encryption key (odd/even) indicator. -### Keying Material +#### Keying Material For each stream, the sender generates a Stream Encrypting Key (SEK) and a Salt (not shown in Figure 1). For the initial implementation and for most envisioned scenarios where no separate authentication algorithm is used for message integrity, the SEK is used directly to encrypt the media stream. The Initial Vector (IV) for the counter is derived from the Salt only. In other scenarios, the SEK can be used along with the Salt as a key generating material to produce distinct encryption, authentication, and salt keys. -### Stream Encrypting Key (SEK) +#### Stream Encrypting Key (SEK) The Stream Encrypting Key (SEK) is pseudo-random and different for each stream. It must be 128, 192, or 256 bits long for the AES-CTR ciphers. It is non-persistent and relatively short lived. In a typical scenario the SEK is expected to last, cryptographically, around 37 days for a 31-bit counter (231 packets / 667 packets/second). The SEK is regenerated every time a stream starts. It must be discarded before 231 packets are encrypted (31-bit packet index) and replaced seamlessly using an odd/even key mechanism described further. SRT is conservative and regenerates the SEK key every 225 packets (~6 hours in the above scenario of a 667 packets per second stream). Reusing an IV (often called nonce) with the same key on different clear text is a known catastrophic issue of counter mode ciphers. By regenerating the SEK each time a stream starts we remove the need for fancier management of the IV to ensure uniqueness. -### Initialization Vector (IV) +#### Initialization Vector (IV) The IV (also named nonce in the AES-CTR context) is a 112 bit random number. For the initial implementation and for most envisioned scenarios where no separate authentication algorithm is used for message integrity (Auth=0), the IV is derived from the salt only. IV = MSB(112, Salt) ; Most significant 112 bits of the salt. -### Counter (ctr) +#### Counter (ctr) The counter for AES-CTR is the size of the cipher’s block, i.e. 128 bits. It is made of a block counter in the least significant 16 bits, counting blocks of a packet, and a 32 bits packet index in the next 32 bits. The upper 112 bits are XORed with the IV to produce a unique counter for each crypto block. ![Figure 2][figure2] Figure 2 The block counter (bctr) is incremented for each cipher block while producing the key stream. The packet index is incremented for each packet submitted to the cipher. The IV is derived from the Salt provided with the Keying Material. -### Keying Material message (KMmsg) +#### Keying Material message (KMmsg) The SEK and a Salt are transported in-stream, in a Keying Material message (KMmsg), implemented as a SRT custom control packet, wrapped with a longer term Key Encrypting Key (KEK) using AES key wrap [RFC3394]. There are possibilities for an eventual key wrapper with integrity such as AESKW [ANSX9.102] or AES-SIV [RFC5297]. Transmitting a key in-band is not original to this specification. It is used in DVB MPEG-TS where the stream encrypting key is transmitted in an Entitlement Control Message (ECM). It is also proposed in an IETF draft for SRTP for Encrypted Key Transport [SRTP-EKT]. The connection-oriented SRT KM ctrl packet is transmitted at the start of the connection, before any data packet. In most case, if the control packet is not lost, the receiver is able to decrypt from the first packet. Otherwise, the initial packets are dropped (or stored for later decryption) until the KM control packet is received. The SRT ctrl packet is retransmitted until acknowledged by the receiver. -### Odd/Even Stream Encrypting Key (oSEK/eSEK) +#### Odd/Even Stream Encrypting Key (oSEK/eSEK) To ensure seamless rekeying for cryptographic (counter exhausted) or access control reasons, a two-key mechanism, similar to the one used with DVB systems is used. The two keys are identified as the odd key and the even key (oSEK/eSEK). Basically, an odd/even flag in the SRT data header tells which key is in use. The next key to use is transmitted in advance (KM Pre-Announce) to the receivers in a SRT ctrl packet. When rekeying occurs, the SRT data header odd/even flag flips and the receiver already have the new key in hand to continue decrypting the stream without missing a packet. -### Key Encrypting Key (KEK) +#### Key Encrypting Key (KEK) The KEK is used by the sender to wrap the SEK and by the receiver to unwrap it and then decrypt the stream. The KEK must be at least the size of the key it protects, the SEK. The KEK is derived from a shared secret, a pre-shared password by default. The KEK is derived with the PBKDF2 [PCKS5] derivation function with the stream Salt and the shared secret for input. Each stream then uses a unique KEK to encrypt its Keying Material. A compromised KEK does not compromise other streams protected with the same shared secret (but a compromised shared secret compromises all streams protected with KEK derived from it). Late derivation of the KEK using stream Salt also permits to generate a KEK of the proper size, based on the size of the key it protects. diff --git a/docs/handshake.md b/docs/features/handshake.md similarity index 99% rename from docs/handshake.md rename to docs/features/handshake.md index 756c538e4..ef3fcfdbe 100644 --- a/docs/handshake.md +++ b/docs/features/handshake.md @@ -3,6 +3,8 @@ Published: 2018-06-28 Last updated: 2018-06-28 +**NOTE:** This document might be outdated, please consult [Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and [Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. + **Contents** - [Overview](#overview) @@ -1333,7 +1335,7 @@ account to calculate the time threshold for `TLPKTDROP`. `KMREQ` and `KMRSP` contain the KMX (key material exchange) message used for encryption. The most important part of this message is the -AES-wrapped key (see the [Encryption documentation](encryption.md) for +AES-wrapped key (see [SRT Encryption](encryption.md) for details). If the encryption process on the Responder side was successful, the response contains the same message for confirmation. Otherwise it's one single 32-bit value that contains the value of `SRT_KMSTATE` type, @@ -1540,8 +1542,4 @@ application should set it on a Caller socket using the `SRTO_STREAMID` option. Upon connection, the accepted socket on the Listener side will have exactly the same value set, and it can be retrieved using the same option. For more details about the prospective use of this option, please refer to the -[SRT API Socket Options](API/API-socket-options.md) and [SRT Access Control guidelines](AccessControl.md). - - -[Return to top of page](#srt-handshake) - +[SRT API Socket Options](../API/API-socket-options.md) and [SRT Access Control (Stream ID) Guidlines](access-control.md). diff --git a/docs/images/block-aligned-5rx10c.png b/docs/features/images/block-aligned-5rx10c.png similarity index 100% rename from docs/images/block-aligned-5rx10c.png rename to docs/features/images/block-aligned-5rx10c.png diff --git a/docs/images/block-aligned.png b/docs/features/images/block-aligned.png similarity index 100% rename from docs/images/block-aligned.png rename to docs/features/images/block-aligned.png diff --git a/docs/images/non-block-aligned-5rx10c-deleted-packets.png b/docs/features/images/non-block-aligned-5rx10c-deleted-packets.png similarity index 100% rename from docs/images/non-block-aligned-5rx10c-deleted-packets.png rename to docs/features/images/non-block-aligned-5rx10c-deleted-packets.png diff --git a/docs/images/non-block-aligned-5rx10c.png b/docs/features/images/non-block-aligned-5rx10c.png similarity index 100% rename from docs/images/non-block-aligned-5rx10c.png rename to docs/features/images/non-block-aligned-5rx10c.png diff --git a/docs/images/non-block-aligned.png b/docs/features/images/non-block-aligned.png similarity index 100% rename from docs/images/non-block-aligned.png rename to docs/features/images/non-block-aligned.png diff --git a/docs/images/packet-filter-mechanism.png b/docs/features/images/packet-filter-mechanism.png similarity index 100% rename from docs/images/packet-filter-mechanism.png rename to docs/features/images/packet-filter-mechanism.png diff --git a/docs/images/rebuild-missing-sequence.png b/docs/features/images/rebuild-missing-sequence.png similarity index 100% rename from docs/images/rebuild-missing-sequence.png rename to docs/features/images/rebuild-missing-sequence.png diff --git a/docs/images/srt-encryption-1.png b/docs/features/images/srt-encryption-1.png similarity index 100% rename from docs/images/srt-encryption-1.png rename to docs/features/images/srt-encryption-1.png diff --git a/docs/images/srt-encryption-2.png b/docs/features/images/srt-encryption-2.png similarity index 100% rename from docs/images/srt-encryption-2.png rename to docs/features/images/srt-encryption-2.png diff --git a/docs/images/staircase-pattern-5rx10c.png b/docs/features/images/staircase-pattern-5rx10c.png similarity index 100% rename from docs/images/staircase-pattern-5rx10c.png rename to docs/features/images/staircase-pattern-5rx10c.png diff --git a/docs/live-streaming.md b/docs/features/live-streaming.md similarity index 95% rename from docs/live-streaming.md rename to docs/features/live-streaming.md index 1ee552cc2..f0662b60f 100644 --- a/docs/live-streaming.md +++ b/docs/features/live-streaming.md @@ -1,15 +1,14 @@ -Live streaming with SRT - guidelines -==================================== +## Live Streaming with SRT - Guidelines + +**NOTE:** See also best practices and configuration tips in [Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). SRT is primarily created for live streaming. Before you use it, you must keep in mind that Live Streaming is a process with its own rules, of which SRT fulfills only and exclusively the transmission part. The Live Streaming process consists of more parts. - -Transmitting MPEG TS binary protocol over SRT -============================================= +## Transmitting MPEG TS binary protocol over SRT MPEG-TS is the most important protocol commonly sent over internet using SRT, and the main reason for initiating this project. @@ -30,9 +29,7 @@ an extra header (this is an option that people often try with RTP) - note that However, the transmission must still satisfy the Live Streaming Requirements. - -Live Streaming Requirements -=========================== +## Live Streaming Requirements The MPEG-TS stream, as a good example, consists of Frames. Each Frame is a portion of data assigned to a particular stream (usually you have @@ -92,9 +89,7 @@ requirement for a live stream is that data must be transmitted with exactly the **average** speed as they are output by a video player. More precisely, the data must be produced at exactly the same speed as they will be consumed by the video player. - -Live Streaming Process -====================== +## Live Streaming Process Now that you know how Live Streaming turns a bunch of MPEG-TS encoded video and audio frames into a network live stream, let's complete the definition of live streaming diff --git a/docs/packet-filtering-and-fec.md b/docs/features/packet-filtering-and-fec.md similarity index 98% rename from docs/packet-filtering-and-fec.md rename to docs/features/packet-filtering-and-fec.md index c834d9934..1f1e04bea 100644 --- a/docs/packet-filtering-and-fec.md +++ b/docs/features/packet-filtering-and-fec.md @@ -52,7 +52,7 @@ The packet filter framework is open for extensions so that users may register their own filters. SRT provides also one built-in filter named "fec". This filter implements the FEC mechanism, as described in SMPTE 2022-1-2007. -![SRT packet filter mechanism](/docs/images/packet-filter-mechanism.png) +![SRT packet filter mechanism](images/packet-filter-mechanism.png) On the input side, filtering occurs at the moment when a packet is extracted from the send buffer. A filter may then do two things: @@ -99,12 +99,12 @@ a number of columns in one series) * **even**: block aligned (default) - columns are arranged in a solid matrix; the first sequence numbers (SNbase) are all contained in one row: - ![Block-aligned Example](/docs/images/block-aligned.png) + ![Block-aligned Example](images/block-aligned.png) * **staircase**: non-block aligned - column starting points are staggered; the first sequence numbers (SNbase) have an offset equivalent to R+1: - ![Non-block-aligned Example](/docs/images/non-block-aligned.png) + ![Non-block-aligned Example](images/non-block-aligned.png) * **arq**: Optional use of the Automatic Repeat Request (ARQ) protocol. The @@ -235,7 +235,7 @@ The rows begin with sequences numbers 500, 510, 520, 530 and 540. But the column begin in staggered fashion, separated by an interval of R+1. The colours represent consecutive FEC groups (note the "staircase" pattern): -![5R x 10C Staircase Pattern](/docs/images/staircase-pattern-5rx10c.png) +![5R x 10C Staircase Pattern](images/staircase-pattern-5rx10c.png) Here is a representation of a series of packets transmitted starting from packet 537 (H = horizontal position; V = vertical position): @@ -304,7 +304,7 @@ the example below, it is likely that the entire missing sequence from 572 to groups (once these are rebuilt, it becomes possible to rebuild packets 572 and 582 via row FEC): -![Rebuild Missing Sequence](/docs/images/rebuild-missing-sequence.png) +![Rebuild Missing Sequence](images/rebuild-missing-sequence.png) Although in a case of even arrangement you still may have a good luck of having a long loss exactly at the border of two column series, the staircase @@ -649,7 +649,7 @@ by deleting the entire matrix. In the figure below, with a matrix size of 5 rows by 10 columns, the green region of column and row groups (series 0) is deleted once a packet (#550 or later) from the red region (series 1) arrives. -![Block Aligned 5R x 10C](/docs/images/block-aligned-5rx10c.png) +![Block Aligned 5R x 10C](images/block-aligned-5rx10c.png) For non block-aligned (staircase) FEC arrangements, a series has a more complex @@ -659,7 +659,7 @@ In the figure below (also a matrix size of 5 rows by 10 columns), we can define packet #500 as base0 - the very first sequence number in a group that is still active. Red then represents series 0, blue series 1, and white series 2: -![Non-block Aligned 5R x 10C](/docs/images/non-block-aligned-5rx10c.png) +![Non-block Aligned 5R x 10C](images/non-block-aligned-5rx10c.png) In a staircase arrangement, the minimum distance between base0 and the first @@ -677,7 +677,7 @@ group - that is, all rows whose first packet falls in the sequence from #500 to #540. In the figure below, deletion of series 0 corresponds to columns with a red background and rows with a red border: -![Non-block Aligned 5R x 10C Deleted Packets](/docs/images/non-block-aligned-5rx10c-deleted-packets.png) +![Non-block Aligned 5R x 10C Deleted Packets](images/non-block-aligned-5rx10c-deleted-packets.png) After the red series 0 is deleted, packet #550 becomes the new base0, packets in diff --git a/docs/socket-groups.md b/docs/features/socket-groups.md similarity index 100% rename from docs/socket-groups.md rename to docs/features/socket-groups.md diff --git a/docs/gstreamer.md b/docs/gstreamer.md deleted file mode 100644 index f02086e5f..000000000 --- a/docs/gstreamer.md +++ /dev/null @@ -1,62 +0,0 @@ -# Using SRT with GStreamer - -Starting from ver. 1.14 GStreamer supports SRT (see the [v.1.14 release notes](https://gstreamer.freedesktop.org/releases/1.14/)). -See the SRT plugin for GStreamer on [git](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/tree/master/ext/srt). - - -## Using GStreamer and SRT to set up a screensharing - -Based on the description in [#7](https://github.com/Haivision/srt/issues/7). Note that the commands are likely to change slightly for gstreamer 1.16 (see this [issue](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/874#note_106395)). - -If you don't want to build GSteamer, SRT, and all the plugins from source or don't have a distribution that has 1.14 readily available, you can use [`nix`](https://nixos.org/nix/) to reproduce what is shown further. - -Simply install `nix`; then use the command bellow to open a shell where the following commands work. - -``` -NIX_PATH=nixpkgs=https://github.com/nh2/nixpkgs/archive/a94ff5f6aaa.tar.gz nix-shell -p gst_all_1.gstreamer \ --p gst_all_1.gst-plugins-good -p gst_all_1.gst-plugins-base -p gst_all_1.gst-plugins-bad \ --p gst_all_1.gst-plugins-ugly -p gst_all_1.gst-libav -``` - -### Sender server - -Set up a sender server that will grab a source raw video from a desktop or a webcam, encode it with x.264 (H.264/AVC) encoder, pack it in `MPEG-TS` ([more info about live streaming](live-streaming.md)). Then pipe it into the SRT sink that sends it over the network to the receiver client. The streaming URI should looks like `uri=srt://:`. In the examples below the streaming is sent to port 888 on a localhost by specifying `uri=srt://0.0.0.0:8888`. - - -##### For screensharing (Linux with X Display) - -The `ximagesrc` GStreamer plugin can be used to capture X Display and create raw RGB video. -Refer to `ximagesrc` [RM](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-ximagesrc.html) for configuration options. - -``` -/usr/bin/time gst-launch-1.0 ximagesrc startx=0 show-pointer=true use-damage=0 ! videoconvert \ -! x264enc bitrate=32000 tune=zerolatency speed-preset=veryfast byte-stream=true threads=1 key-int-max=15 \ -intra-refresh=true ! video/x-h264, profile=baseline, framerate=30/1 ! mpegtsmux \ -! srtserversink uri=srt://0.0.0.0:8888/ latency=100 -``` - -##### For webcam images - -The `v4l2src` GStreamer plugin can be used to capture video from v4l2 devices, like webcams and TV cards. -Refer to `v4l2src` [RM](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-v4l2src.html) for further information. - -``` -/usr/bin/time gst-launch-1.0 v4l2src ! videoconvert ! x264enc bitrate=8000 tune=zerolatency speed-preset=superfast \ -byte-stream=true threads=1 key-int-max=15 intra-refresh=true ! video/x-h264, profile=baseline ! mpegtsmux \ -! srtserversink uri=srt://0.0.0.0:8888/ latency=100 -``` - -##### Notes - -* The `decodebin` can also be used to configure settings automatically. Using explicit pipeline elements here make it possible to tune the settings when needed. -* A use of `time` helps to determine when the thread is capped at 100%, while the the `thread=1` parameter makes the encoding use only one thread. Remove `threads=1` to allow multiple cores, or cjange the `speed-preset` to reduce CPU load. -* The `timeout` setting can be tuned. A recommended timeout is 2x-2.5x of the expected roundtrip time. -* The password functionality works as well, but only if a password is `>= 10` characters long; otherwise it's completely ignored. See [this bug](https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/694#note_106616) of GStreamer. - -### Receiver client - -A client connection over SRT to the server with URI `srt://127.0.0.1:8888` (localhost) or a remote server is set up. URI syntax is `srt://:`. Then MPEG-TS demuxer and video decoder is used to get a decompressed video, that goes to a playback plugin `autovideosink`. Note that multiple clients can connect to the server started earlier. - -`gst-launch-1.0 srtclientsrc uri=srt://127.0.0.1:8888 ! tsdemux ! h264parse ! video/x-h264 ! avdec_h264 ! autovideosink sync=false` - -This works over both the internet and localhost. diff --git a/docs/images/SRT_History_Good_Signal.png b/docs/misc/images/srt-history-good-signal.png similarity index 100% rename from docs/images/SRT_History_Good_Signal.png rename to docs/misc/images/srt-history-good-signal.png diff --git a/docs/images/SRT_Transmission_Bad_Signal.png b/docs/misc/images/srt-transmission-bad-signal.png similarity index 100% rename from docs/images/SRT_Transmission_Bad_Signal.png rename to docs/misc/images/srt-transmission-bad-signal.png diff --git a/docs/why-srt-was-created.md b/docs/misc/why-srt-was-created.md similarity index 97% rename from docs/why-srt-was-created.md rename to docs/misc/why-srt-was-created.md index 0d3e35e23..cc694e5df 100644 --- a/docs/why-srt-was-created.md +++ b/docs/misc/why-srt-was-created.md @@ -15,14 +15,14 @@ Having had a history with UDT for data transmission, I remembered its packet los We started testing sending low latency live streams back and forth between Germany and Montreal and it worked! However, we didn't get the latency down to a level we had hoped to achieve. The problem we faced turned out to be timing related (as always in media ...). What happened was this: -![Bad Signal](images/SRT_Transmission_Bad_Signal.png) +![Bad Signal](images/srt-transmission-bad-signal.png) The characteristics of the original stream on the source network got completely changed by the transmission over the public internet. The reasons are delay, jitter, packet loss and its recovery on the dirty network. The signal on the receiver side had completely different characteristics, which led to problems with decoding, as the audio and video decoders didn't get the packets at the expected times. This can be handled by buffering, but that's not what you want in low latency setups. The solution was to come up with a mechanism that recreates the signal characteristics on the receiver side. That way we were able to dramatically reduce the buffering. This functionality is part of the SRT protocol itself, so once the data comes out of the SRT protocol on the receiver side, the stream characteristics have been properly recovered. The result is a happy decoder: -![Good Signal](images/SRT_History_Good_Signal.png) +![Good Signal](images/srt-history-good-signal.png) We publicly showed SRT (Secure Reliable Transport) the first time at IBC 2013, where we were the only ones to show an HEVC encoded live stream, camera to glass, from a hotel suite outside the exhibition directly onto the show floor, using the network provided by the RAI. Everybody who has been at such a show before knows how bad these networks can get. And the network was bad. So bad that we expected the whole demo to fall apart, having pulled the first trial version of SRT directly from the labs. The excitement was huge, when we realized that the transmission still worked fine! diff --git a/scripts/build-android/README.md b/scripts/build-android/README.md new file mode 100644 index 000000000..d225ed022 --- /dev/null +++ b/scripts/build-android/README.md @@ -0,0 +1,5 @@ +## Scripts for building SRT for Android + +**NOTE:** The scripts have been moved from `docs/Android/` folder. Updating the paths might be required. + +See [Building SRT for Android](../../docs/build/build-android.md) for the instructions. diff --git a/docs/Android/mkall b/scripts/build-android/mkall similarity index 100% rename from docs/Android/mkall rename to scripts/build-android/mkall diff --git a/docs/Android/mksrt b/scripts/build-android/mksrt similarity index 100% rename from docs/Android/mksrt rename to scripts/build-android/mksrt diff --git a/docs/Android/mkssl b/scripts/build-android/mkssl similarity index 100% rename from docs/Android/mkssl rename to scripts/build-android/mkssl diff --git a/docs/Android/packjni b/scripts/build-android/packjni similarity index 100% rename from docs/Android/packjni rename to scripts/build-android/packjni diff --git a/docs/Android/prepare_build b/scripts/build-android/prepare_build similarity index 100% rename from docs/Android/prepare_build rename to scripts/build-android/prepare_build diff --git a/srtcore/api.h b/srtcore/api.h index cd467a978..604f14a99 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -70,7 +70,7 @@ modified by #endif // Please refer to structure and locking information provided in the -// LowLevelInfo.md document. +// docs/dev/low-level-info.md document. class CUDT; From 98649a6457a80dca28d6cfe5165721d9c6a7072d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 9 Apr 2021 17:15:20 +0200 Subject: [PATCH 023/683] [core] Fixed SRTO_KM* options setting. (#1922) - Rejecting negative values of SRTO_KM* in srt_setsockopt. - Handling zero value properly (as the default). - SRTO_KM* options are now readable as was stated in docs. - Updated docs. - Removed unused func argument (processCtrlShutdown). --- docs/API/API-socket-options.md | 144 +++++++++++++++++---------------- srtcore/core.cpp | 14 +++- srtcore/core.h | 4 +- srtcore/socketconfig.h | 40 ++++++--- 4 files changed, 119 insertions(+), 83 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2680e060f..c1d4420e1 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -193,67 +193,67 @@ The + marker can only coexist with GS. Possible specifications are: The following table lists SRT API socket options in alphabetical order. Option details are given further below. -| Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | -| :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :-----------: | :------: |:---:|:-----:| -| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | -| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | -| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | -| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | -| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | -| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | -| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | -| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | -| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | -| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | -| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | -| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0x1000000 | 0.. | RW | GSD | -| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | -| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | -| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | -| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | -| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | -| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | -| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | -| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | -| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | -| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | -| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | -| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | -| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | -| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | -| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | -| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | -| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | -| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | +| Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | +| :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :---------------: | :------: |:---:|:-----:| +| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | +| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | +| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | +| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | +| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | +| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | +| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | +| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | +| [`SRTO_GROUPSTABTIMEO`](#SRTO_GROUPSTABTIMEO) | 1.5.0 | pre | `int32_t` | ms | 80 | 10-... | W | GSD+ | +| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | +| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | +| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | +| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | +| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | +| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | +| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | +| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | +| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | +| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | +| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | +| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | +| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | +| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | +| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | +| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | +| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | +| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | +| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | +| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | +| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | +| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | +| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | +| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | +| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | +| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | +| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | +| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | +| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | +| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | +| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | +| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions @@ -623,9 +623,9 @@ used in any regular development.* #### SRTO_KMPREANNOUNCE -| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000 | 0.. * | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | +| `SRTO_KMPREANNOUNCE` | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | The interval (defined in packets) between when a new Stream Encrypting Key (SEK) is sent and when switchover occurs. This value also applies to the @@ -642,20 +642,24 @@ retransmitted packets. The old key is decommissioned at `SRTO_KMPREANNOUNCE` packets after switchover. -The allowed range for this value is between 1 and half of the current value of +**The allowed range** for this value is between 1 and half of the current value of `SRTO_KMREFRESHRATE`. The minimum value should never be less than the flight -window (i.e. the number of packets that have already left the sender but have +window [`SRTO_FC`](#SRTO_FC) (i.e. the number of packets that have already left the sender but have not yet arrived at the receiver). +The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. + +**Default value:** `0` - corresponds to 4096 packets (212 or 0x1000). + [Return to list](#list-of-options) --- #### SRTO_KMREFRESHRATE -| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | -| --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | -| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0x1000000| 0.. | RW | GSD | +| OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | +| --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | +| `SRTO_KMREFRESHRATE` | 1.3.2 | pre | `int32_t` | pkts | 0: 224| 0.. | RW | GSD | The number of packets to be transmitted after which the Stream Encryption Key (SEK), used to encrypt packets, will be switched to the new one. Note that @@ -667,6 +671,8 @@ at the receiver before the first packet encrypted with the new SEK is received. The old key remains active after switchover in order to decrypt packets that might still be in flight, or packets that have to be retransmitted. +**Default value:** `0` - corresponds to 16777216 packets (224 or 0x1000000). + [Return to list](#list-of-options) --- diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 381669b30..a3d6b217f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -708,6 +708,16 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) *(int *)optval = (int) m_config.zExpPayloadSize; break; + case SRTO_KMREFRESHRATE: + optlen = sizeof(int); + *(int*)optval = (int)m_config.uKmRefreshRatePkt; + break; + + case SRTO_KMPREANNOUNCE: + optlen = sizeof(int); + *(int*)optval = (int)m_config.uKmPreAnnouncePkt; + break; + #if ENABLE_EXPERIMENTAL_BONDING case SRTO_GROUPCONNECT: optlen = sizeof (int); @@ -8399,7 +8409,7 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void CUDT::processCtrlShutdown(const CPacket& ctrlpkt) +void CUDT::processCtrlShutdown() { m_bShutdown = true; m_bClosing = true; @@ -8486,7 +8496,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - processCtrlShutdown(ctrlpkt); + processCtrlShutdown(); break; case UMSG_DROPREQ: // 111 - Msg drop request diff --git a/srtcore/core.h b/srtcore/core.h index 6be1a9011..982408c8b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -970,9 +970,7 @@ class CUDT void processCtrlDropReq(const CPacket& ctrlpkt); /// @brief Process incoming shutdown control packet - /// @param ctrlpkt incoming shutdown packet - void processCtrlShutdown(const CPacket& ctrlpkt); - + void processCtrlShutdown(); /// @brief Process incoming user defined control packet /// @param ctrlpkt incoming user defined packet void processCtrlUserDefined(const CPacket& ctrlpkt); diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 29105808b..969d624e5 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -1002,14 +1002,28 @@ struct CSrtConfigSetter { using namespace srt_logging; - // If you first change the KMREFRESHRATE, KMPREANNOUNCE - // will be set to the maximum allowed value - co.uKmRefreshRatePkt = cast_optval(optval, optlen); - if (co.uKmPreAnnouncePkt == 0 || co.uKmPreAnnouncePkt > (co.uKmRefreshRatePkt - 1) / 2) + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value + co.uKmRefreshRatePkt = (unsigned) val; + + if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) + return; // Both values are default + + const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; + const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + + if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) { - co.uKmPreAnnouncePkt = (co.uKmRefreshRatePkt - 1) / 2; + co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << co.uKmRefreshRatePkt << ": setting SRTO_KMPREANNOUNCE=0x" + log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" << std::hex << co.uKmPreAnnouncePkt); } } @@ -1023,11 +1037,19 @@ struct CSrtConfigSetter using namespace srt_logging; const int val = cast_optval(optval, optlen); - const int kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - if (val > (kmref - 1) / 2) + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; + const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << val << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } From c3864aac7cbf4ee07a3e4948e04274cd577e59da Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Mon, 12 Apr 2021 03:49:26 -0500 Subject: [PATCH 024/683] [apps] Fix android build NDK r16b and earlier. (#1923) --- apps/apputil.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 6031668ba..f7bf83df3 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -58,6 +58,19 @@ inline void SysCleanupNetwork() #include #include +// Fixes Android build on NDK r16b and earlier. +#if defined(__ANDROID__) && (__ANDROID__ == 1) + #include + #if !defined(__NDK_MAJOR__) || (__NDK_MAJOR__ <= 16) + struct ip_mreq_sourceFIXED { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; + struct in_addr imr_sourceaddr; + }; + #define ip_mreq_source ip_mreq_sourceFIXED + #endif +#endif + // Nothing needs to be done on POSIX; this is a Windows problem. inline bool SysInitializeNetwork() {return true;} inline void SysCleanupNetwork() {} From e63edadca6559dc7e79fa4ddcc8217173e124574 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 31 Mar 2021 16:31:48 +0800 Subject: [PATCH 025/683] [build] Fix pc file generation when use mbedtls mbedtls doesn't create .pc file by itself. Put mbedtls under Requires.private leads to pkg-config check error. --- CMakeLists.txt | 51 ++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c37bdc41..25f34c881 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,14 +283,29 @@ if (ENABLE_ENCRYPTION) link_directories( ${SSL_LIBRARY_DIRS} ) - else() # Common for mbedtls and openssl - if ("${USE_ENCLIB}" STREQUAL "mbedtls") - add_definitions(-DUSE_MBEDTLS=1) - set (SSL_REQUIRED_MODULES "mbedtls mbedcrypto") - else() - add_definitions(-DUSE_OPENSSL=1) - set (SSL_REQUIRED_MODULES "openssl libcrypto") + elseif ("${USE_ENCLIB}" STREQUAL "mbedtls") + add_definitions(-DUSE_MBEDTLS=1) + if ("${SSL_LIBRARY_DIRS}" STREQUAL "") + set(MBEDTLS_PREFIX "${CMAKE_PREFIX_PATH}" CACHE PATH "The path of mbedtls") + find_package(MbedTLS REQUIRED) + set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) + endif() + if ("${SSL_LIBRARIES}" STREQUAL "") + set (SSL_LIBRARIES mbedtls mbedcrypto) endif() + message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") + + foreach(LIB ${SSL_LIBRARIES}) + if(IS_ABSOLUTE ${LIB} AND EXISTS ${LIB}) + set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ${LIB}) + else() + set(SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} "-l${LIB}") + endif() + endforeach() + else() # openssl + add_definitions(-DUSE_OPENSSL=1) + set (SSL_REQUIRED_MODULES "openssl libcrypto") # Try using pkg-config method first if enabled, # fall back to find_package method otherwise if (USE_OPENSSL_PC) @@ -315,24 +330,12 @@ if (ENABLE_ENCRYPTION) ) message(STATUS "SSL via pkg-config: -L ${SSL_LIBRARY_DIRS} -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") else() - if ("${USE_ENCLIB}" STREQUAL "mbedtls") - if ("${SSL_LIBRARY_DIRS}" STREQUAL "") - set(MBEDTLS_PREFIX "${CMAKE_PREFIX_PATH}" CACHE PATH "The path of mbedtls") - find_package(MbedTLS REQUIRED) - set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) - endif() - if ("${SSL_LIBRARIES}" STREQUAL "") - set (SSL_LIBRARIES mbedtls mbedcrypto) - endif() - message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") - else() - find_package(OpenSSL REQUIRED) - set (SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) - set (SSL_LIBRARIES ${OPENSSL_LIBRARIES}) - message(STATUS "SSL via find_package(OpenSSL): -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") - endif() + find_package(OpenSSL REQUIRED) + set (SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) + set (SSL_LIBRARIES ${OPENSSL_LIBRARIES}) + message(STATUS "SSL via find_package(OpenSSL): -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") endif() + endif() add_definitions(-DSRT_ENABLE_ENCRYPTION) From 009b7b62f793718f621050654beb080d644894c8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 13:04:00 +0200 Subject: [PATCH 026/683] [core] Fixed SRTO_SNDDROPDELAY: use POST restriction --- apps/socketoptions.hpp | 2 +- srtcore/core.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 2a14c3854..06c929896 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -229,7 +229,7 @@ const SocketOption srt_options [] { { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr}, { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "tlpktdrop", 0, SRTO_TLPKTDROP, SocketOption::PRE, SocketOption::BOOL, nullptr}, - { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::PRE, SocketOption::INT, nullptr}, + { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::POST, SocketOption::INT, nullptr}, { "nakreport", 0, SRTO_NAKREPORT, SocketOption::PRE, SocketOption::BOOL, nullptr}, { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, { "drifttracer", 0, SRTO_DRIFTTRACER, SocketOption::POST, SocketOption::BOOL, nullptr}, diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a3d6b217f..795f0a727 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -161,19 +161,19 @@ struct SrtOptionAction flags[SRTO_SENDER] = SRTO_R_PRE; flags[SRTO_TSBPDMODE] = SRTO_R_PRE; flags[SRTO_LATENCY] = SRTO_R_PRE; - flags[SRTO_INPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_MININPUTBW] = 0 | SRTO_POST_SPEC; - flags[SRTO_OHEADBW] = 0 | SRTO_POST_SPEC; + flags[SRTO_INPUTBW] = SRTO_POST_SPEC; + flags[SRTO_MININPUTBW] = SRTO_POST_SPEC; + flags[SRTO_OHEADBW] = SRTO_POST_SPEC; flags[SRTO_PASSPHRASE] = SRTO_R_PRE; flags[SRTO_PBKEYLEN] = SRTO_R_PRE; flags[SRTO_IPTTL] = SRTO_R_PREBIND; flags[SRTO_IPTOS] = SRTO_R_PREBIND; flags[SRTO_TLPKTDROP] = SRTO_R_PRE; - flags[SRTO_SNDDROPDELAY] = SRTO_R_PRE; + flags[SRTO_SNDDROPDELAY] = SRTO_POST_SPEC; flags[SRTO_NAKREPORT] = SRTO_R_PRE; flags[SRTO_VERSION] = SRTO_R_PRE; flags[SRTO_CONNTIMEO] = SRTO_R_PRE; - flags[SRTO_LOSSMAXTTL] = 0 | SRTO_POST_SPEC; + flags[SRTO_LOSSMAXTTL] = SRTO_POST_SPEC; flags[SRTO_RCVLATENCY] = SRTO_R_PRE; flags[SRTO_PEERLATENCY] = SRTO_R_PRE; flags[SRTO_MINVERSION] = SRTO_R_PRE; From 7809e20da43755417e17ca231afb17e121b0d651 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 14:32:43 +0200 Subject: [PATCH 027/683] [build] Updated Travis OSX to 11.1 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82c16d99c..c2e2205c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ addons: token: secure: "wJZC0kyyjuf4SZyonZ6p/5Ga9asEqSnKWF9NpRbu6S6ceERO7vbebuSJF5qX3A6ivPuw0TTk5WASOdnvIyfA28FU/D0MWRdH8K7T3w77wdE9EgAEYTUXzdrbzJY18+9pxjljHwWXWALPSGf3MClg4irWrdk1e6uHK+68R39+ZvBGBFpWeeZy/+at9+xwhtAGKBlSHe8zc+3wPxuYdvviLVJ25qbpNmnzkUR0X89G+UBl90raCPSN32EHFdImHZ5DxfEQQJgZFRjzQUY4EW/iYwaMel7jufAq0ClgV4psKujl9Lz8cPqx3WgqRfJyiIthOMTsac7G4zAw8LK2CI0VsssBp0JalLXaumi6vG7o6c3rIwKckzSKccq3pHa7h45praIVVn9s3nq+Q/JGA11FMkKQxdQtmwgFsLhbi6ZxabgsUi5KtWoWY2z6MgpJuROuAjNxZi9XJzUoJs7zSTUtRRW7V8Q2lRiOnknYh25N6TCA5bpyy1EZmRdJErm071YNI9P01gbFz5137FWJFiJzro9TGF0KoHSGiCIdUt3WlMzwr/i/wFLxFBQOZQ2rjTXvhs4hxONxMZV3gzxA1NdLaf9i5Mh6jxVMV+ujaRSV7JmPGzxqiAlpT9cJUhTCYuar9diLLeDrpe7RawEZR8V1xVDQ7yT8ruDNQ78VbSn/sC0=" homebrew: - update: true # TODO: this should be removed once this bug is fixed: https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296 + update: false packages: - openssl @@ -32,10 +32,10 @@ matrix: - os: linux env: BUILD_TYPE=Release - os: osx - osx_image: xcode10.2 + osx_image: xcode11.1 env: BUILD_TYPE=Debug - os: osx - osx_image: xcode10.2 + osx_image: xcode11.1 env: BUILD_TYPE=Release - os: linux compiler: x86_64-w64-mingw32-g++ From 66f2390ca04deea2626700247994ac2dac82d254 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Apr 2021 14:26:35 +0200 Subject: [PATCH 028/683] [tests] Removed "" _S function. On some platforms _S is defined as a macro. --- test/test_ipv6.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 2ca256f1d..677cae180 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -4,11 +4,6 @@ #include "srt.h" #include "netinet_any.h" -inline std::string operator "" _S(const char* src, std::size_t) -{ - return std::string(src); -} - class TestIPv6 : public ::testing::Test { @@ -94,12 +89,12 @@ class TestIPv6 void PrintAddresses(SRTSOCKET sock, const char* who) { sockaddr_any sa; - int sa_len = sa.storage_size(); + int sa_len = (int) sa.storage_size(); srt_getsockname(sock, sa.get(), &sa_len); ShowAddress(std::string(who) + " Sock name: ", sa); //std::cout << who << " Sock name: " << << sa.str() << std::endl; - sa_len = sa.storage_size(); + sa_len = (int) sa.storage_size(); srt_getpeername(sock, sa.get(), &sa_len); //std::cout << who << " Peer name: " << << sa.str() << std::endl; ShowAddress(std::string(who) + " Peer name: ", sa); @@ -121,10 +116,10 @@ TEST_F(TestIPv6, v4_calls_v6_mapped) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200"); client.join(); } @@ -138,10 +133,10 @@ TEST_F(TestIPv6, v6_calls_v6_mapped) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::1:4200"); client.join(); } @@ -158,10 +153,10 @@ TEST_F(TestIPv6, v6_calls_v6) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "::1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "::1:4200"); client.join(); } @@ -177,10 +172,10 @@ TEST_F(TestIPv6, v6_calls_v4) ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); - std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"_S); + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"); const sockaddr_any sa_accepted = DoAccept(); - EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200"_S); + EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200"); client.join(); } From 6a77c0470352f47accd3ceab0357c9173b691823 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 13 Apr 2021 16:47:43 +0800 Subject: [PATCH 029/683] [build] Fix cmake warning on FindMbedTLS (#1935) The warning message: "The package name passed to `find_package_handle_standard_args` (Libmbedtls) does not match the name of the calling package (MbedTLS). This can lead to problems in calling code that expects `find_package` result variables (e.g., `_FOUND`) to follow a certain pattern." --- scripts/FindMbedTLS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/FindMbedTLS.cmake b/scripts/FindMbedTLS.cmake index 7dca6e47e..662255622 100644 --- a/scripts/FindMbedTLS.cmake +++ b/scripts/FindMbedTLS.cmake @@ -111,5 +111,5 @@ endif() # Now we've accounted for the 3-vs-1 library case: include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libmbedtls DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) +find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) From 0f4c32e40a250db9863b6bdaa09a64f8f871f307 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 13 Apr 2021 10:48:03 +0200 Subject: [PATCH 030/683] [apps] Changed the default log level to Warn (#1934) --- apps/srt-live-transmit.cpp | 4 ++-- docs/apps/srt-live-transmit.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 4288f351e..bfe4ff590 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -290,7 +290,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) PrintOptionHelp(o_statsout, "", "output stats to file"); PrintOptionHelp(o_statspf, "", "stats printing format {json, csv, default}"); PrintOptionHelp(o_statsfull, "", "full counters in stats-report (prints total statistics)"); - PrintOptionHelp(o_loglevel, "", "log level {fatal,error,info,note,warning}"); + PrintOptionHelp(o_loglevel, "", "log level {fatal,error,warn,note,info,debug}"); PrintOptionHelp(o_logfa, "", "log functional area (see '-h logging' for more info)"); //PrintOptionHelp(o_log_internal, "", "use internal logger"); PrintOptionHelp(o_logfile, "", "write logs to file"); @@ -345,7 +345,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) } cfg.full_stats = OptionPresent(params, o_statsfull); - cfg.loglevel = SrtParseLogLevel(Option(params, "error", o_loglevel)); + cfg.loglevel = SrtParseLogLevel(Option(params, "warn", o_loglevel)); cfg.logfas = SrtParseLogFA(Option(params, "", o_logfa)); cfg.log_internal = OptionPresent(params, o_log_internal); cfg.logfile = Option(params, o_logfile); diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index a124c631c..184bb32a7 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -369,7 +369,7 @@ shell (using **"** **"** quotes or backslash). - **-statsout** - SRT statistics output: filename. Without this option specified, the statistics will be printed to the standard output. - **-pf**, **-statspf** - SRT statistics print format. Values: json, csv, default. After a comma, options can be specified (e.g. "json,pretty"). - **-s**, **-stats**, **-stats-report-frequency** - The frequency of SRT statistics collection, based on the number of packets. -- **-loglevel** - lowest logging level for SRT, one of: *fatal, error, warning, note, debug* (default: *error*) +- **-loglevel** - lowest logging level for SRT, one of: *fatal, error, warn, note, debug* (default: *warn*) - **-logfa, -lfa** - selected FAs in SRT to be logged (default: all are enabled). See the list of FAs running `-help:logging`. - **-logfile:logs.txt** - Output of logs is written to file logs.txt instead of being printed to `stderr`. - **-help, -h** - Show help. From 65d5483162d3df4d974d93592b2d8df435c7a658 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 13 Apr 2021 17:13:58 +0800 Subject: [PATCH 031/683] [core] Use time_point in debugTraceJitter(..) (#1912) --- srtcore/buffer.cpp | 18 +++++++++--------- srtcore/buffer.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 89c2dab72..5ca443485 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -2064,15 +2064,15 @@ int CRcvBuffer::readMsg(char* data, int len, SRT_MSGCTRL& w_msgctl, int upto) } #ifdef SRT_DEBUG_TSBPD_OUTJITTER -void CRcvBuffer::debugTraceJitter(int64_t rplaytime) +void CRcvBuffer::debugTraceJitter(time_point playtime) { - uint64_t now = CTimer::getTime(); - if ((now - rplaytime) / 10 < 10) - m_ulPdHisto[0][(now - rplaytime) / 10]++; - else if ((now - rplaytime) / 100 < 10) - m_ulPdHisto[1][(now - rplaytime) / 100]++; - else if ((now - rplaytime) / 1000 < 10) - m_ulPdHisto[2][(now - rplaytime) / 1000]++; + uint64_t ms = count_microseconds(steady_clock::now() - playtime); + if (ms / 10 < 10) + m_ulPdHisto[0][ms / 10]++; + else if (ms / 100 < 10) + m_ulPdHisto[1][ms / 100]++; + else if (ms / 1000 < 10) + m_ulPdHisto[2][ms / 1000]++; else m_ulPdHisto[3][1]++; } @@ -2105,7 +2105,7 @@ bool CRcvBuffer::accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playt // so in one "unit". w_p = w_q = m_iStartPos; - debugTraceJitter(w_playtime); + debugTraceJitter(play_time); } } else diff --git a/srtcore/buffer.h b/srtcore/buffer.h index f0a499fd4..d998fd1b9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -380,10 +380,10 @@ class CRcvBuffer bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance); #ifdef SRT_DEBUG_TSBPD_OUTJITTER - void debugTraceJitter(int64_t); + void debugTraceJitter(time_point t); #else - void debugTraceJitter(int64_t) {} -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + void debugTraceJitter(time_point) {} +#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } From 9e6c90f8cca832b866620959bb3407ef02cdfddb Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:17:39 +0200 Subject: [PATCH 032/683] [core] Minor refactoring around ACK processing (#1928) --- srtcore/core.cpp | 38 +++++++++++++++------------- srtcore/core.h | 66 +++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 795f0a727..d73acfc26 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -221,7 +221,9 @@ void CUDT::construct() m_pSndLossList = NULL; m_pRcvLossList = NULL; m_iReorderTolerance = 0; - m_iConsecEarlyDelivery = 0; // how many times so far the packet considered lost has been received before TTL expires + // How many times so far the packet considered lost has been received + // before TTL expires. + m_iConsecEarlyDelivery = 0; m_iConsecOrderedDelivery = 0; m_pSndQueue = NULL; @@ -229,7 +231,8 @@ void CUDT::construct() m_pSNode = NULL; m_pRNode = NULL; - m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; // Will be reset to 0 for HSv5, this value is important for HSv4 + // Will be reset to 0 for HSv5, this value is important for HSv4. + m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; // Initial status m_bOpened = false; @@ -239,12 +242,12 @@ void CUDT::construct() m_bClosing = false; m_bShutdown = false; m_bBroken = false; - // XXX m_iBrokenCounter should be still set to some default! + // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; m_tsLastReqTime = steady_clock::time_point(); m_SrtHsSide = HSD_DRAW; - m_uPeerSrtVersion = 0; // not defined until connected. + m_uPeerSrtVersion = 0; // Not defined until connected. m_iTsbPdDelay_ms = 0; m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; @@ -254,10 +257,10 @@ void CUDT::construct() m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; - // Initilize mutex and condition variables + // Initilize mutex and condition variables. initSynch(); - // XXX: Unblock, when the callback is implemented + // TODO: Uncomment when the callback is implemented. // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } @@ -5433,17 +5436,18 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // And of course, it is connected. m_bConnected = true; - // register this socket for receiving data packets + // Register this socket for receiving data packets. m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); // Save the handshake in m_ConnRes in case when needs repeating. m_ConnRes = w_hs; - // send the response to the peer, see listen() for more discussions about this - // XXX Here create CONCLUSION RESPONSE with: + // Send the response to the peer, see listen() for more discussions + // about this. + // TODO: Here create CONCLUSION RESPONSE with: // - just the UDT handshake, if HS_VERSION_UDT4, - // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP + // - if higher, the UDT handshake, the SRT HSRSP, the SRT KMRSP. size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. @@ -7711,8 +7715,8 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) if (m_uPeerSrtVersion == SrtVersion(1, 0, 2)) { data[ACKD_RCVRATE] = rcvRate; // bytes/sec - data[ACKD_XMRATE] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec - ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102; + data[ACKD_XMRATE_VER102_ONLY] = data[ACKD_BANDWIDTH] * m_iMaxSRTPayloadSize; // bytes/sec + ctrlsz = ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_VER102_ONLY; } else if (m_uPeerSrtVersion >= SrtVersion(1, 0, 3)) { @@ -8001,10 +8005,10 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point * Additional UDT fields, not always attached: * ACKD_RCVSPEED * ACKD_BANDWIDTH - * SRT extension version 1.0.2 (bstats): + * SRT extension since v1.0.1: * ACKD_RCVRATE - * SRT extension version 1.0.4: - * ACKD_XMRATE + * SRT extension in v1.0.2 only: + * ACKD_XMRATE_VER102_ONLY */ if (acksize > ACKD_TOTAL_SIZE_SMALL) @@ -8014,7 +8018,7 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point int bandwidth = ackdata[ACKD_BANDWIDTH]; int bytesps; - /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE]) and delivery rate (pcData[ACKD_RCVRATE]) in + /* SRT v1.0.2 Bytes-based stats: bandwidth (pcData[ACKD_XMRATE_VER102_ONLY]) and delivery rate (pcData[ACKD_RCVRATE]) in * bytes/sec instead of pkts/sec */ /* SRT v1.0.3 Bytes-based stats: only delivery rate (pcData[ACKD_RCVRATE]) in bytes/sec instead of pkts/sec */ if (acksize > ACKD_TOTAL_SIZE_UDTBASE) @@ -8025,8 +8029,6 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth); m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps); m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps); - // XXX not sure if ACKD_XMRATE is of any use. This is simply - // calculated as ACKD_BANDWIDTH * m_iMaxSRTPayloadSize. // Update Estimated Bandwidth and packet delivery rate // m_iRcvRate = m_iDeliveryRate; diff --git a/srtcore/core.h b/srtcore/core.h index 982408c8b..70dbb2f7d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -76,7 +76,7 @@ modified by #include -// XXX Utility function - to be moved to utilities.h? +// TODO: Utility function - to be moved to utilities.h? template inline T CountIIR(T base, T newval, double factor) { @@ -87,32 +87,30 @@ inline T CountIIR(T base, T newval, double factor) return base+T(diff*factor); } -// XXX Probably a better rework for that can be done - this can be -// turned into a serializable structure, just like it's for CHandShake. +// TODO: Probably a better rework for that can be done - this can be +// turned into a serializable structure, just like it's done for CHandShake. enum AckDataItem { - ACKD_RCVLASTACK = 0, - ACKD_RTT = 1, - ACKD_RTTVAR = 2, - ACKD_BUFFERLEFT = 3, - ACKD_TOTAL_SIZE_SMALL = 4, - - // Extra fields existing in UDT (not always sent) - - ACKD_RCVSPEED = 4, // length would be 16 - ACKD_BANDWIDTH = 5, - ACKD_TOTAL_SIZE_UDTBASE = 6, // length = 24 - // Extra stats for SRT - - ACKD_RCVRATE = 6, - ACKD_TOTAL_SIZE_VER101 = 7, // length = 28 - ACKD_XMRATE = 7, // XXX This is a weird compat stuff. Version 1.1.3 defines it as ACKD_BANDWIDTH*m_iMaxSRTPayloadSize when set. Never got. - // XXX NOTE: field number 7 may be used for something in future, need to confirm destruction of all !compat 1.0.2 version - - ACKD_TOTAL_SIZE_VER102 = 8, // 32 -// FEATURE BLOCKED. Probably not to be restored. -// ACKD_ACKBITMAP = 8, - ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102 // length = 32 (or more) + ACKD_RCVLASTACK = 0, + ACKD_RTT = 1, + ACKD_RTTVAR = 2, + ACKD_BUFFERLEFT = 3, + ACKD_TOTAL_SIZE_SMALL = 4, // Size of the Small ACK, packet length = 16. + + // Extra fields for Full ACK. + ACKD_RCVSPEED = 4, + ACKD_BANDWIDTH = 5, + ACKD_TOTAL_SIZE_UDTBASE = 6, // Packet length = 24. + + // Extra stats since SRT v1.0.1. + ACKD_RCVRATE = 6, + ACKD_TOTAL_SIZE_VER101 = 7, // Packet length = 28. + + // Only in SRT v1.0.2. + ACKD_XMRATE_VER102_ONLY = 7, + ACKD_TOTAL_SIZE_VER102_ONLY = 8, // Packet length = 32. + + ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102_ONLY // The maximum known ACK length is 32 bytes. }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); @@ -267,14 +265,15 @@ class CUDT // Parameters // - // Note: use notation with X*1000*1000*... instead of million zeros in a row - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; - static const size_t ACK_WND_SIZE = 1024; + // NOTE: Use notation with X*1000*1000*... instead of + // million zeros in a row. + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; int handshakeVersion() { @@ -745,7 +744,6 @@ class CUDT int m_iDeliveryRate; // Packet arrival rate at the receiver side int m_iByteDeliveryRate; // Byte arrival rate at the receiver side - CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state From 109f667cf42b2ba7311000ff2c551b72a4069f80 Mon Sep 17 00:00:00 2001 From: quink-black Date: Wed, 14 Apr 2021 20:58:37 +0800 Subject: [PATCH 033/683] [core] Minor: file scope for global var and func (#1938) 1. sync_posix: make global variable and function file scope 2. replace static_assert by SRT_STATIC_ASSERT --- srtcore/core.cpp | 4 +--- srtcore/sync_posix.cpp | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d73acfc26..cb71cfb6d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2027,9 +2027,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t return SRT_CMD_REJECT; } -#if HAVE_CXX11 - static_assert(SRT_HS_E_SIZE == SRT_HS_LATENCY + 1, "Assuming latency is the last field"); -#endif + SRT_STATIC_ASSERT(SRT_HS_E_SIZE == SRT_HS_LATENCY + 1, "Assuming latency is the last field"); if (bytelen < (SRT_HS_E_SIZE * sizeof(uint32_t))) { // Handshake extension message includes VERSION, FLAGS and LATENCY diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 56a89d6b9..997cf534c 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -77,7 +77,7 @@ void rdtsc(uint64_t& x) #endif } -int64_t get_cpu_frequency() +static int64_t get_cpu_frequency() { int64_t frequency = 1; // 1 tick per microsecond. @@ -123,9 +123,9 @@ static int count_subsecond_precision(int64_t ticks_per_us) return signs; } -const int64_t s_clock_ticks_per_us = get_cpu_frequency(); +static const int64_t s_clock_ticks_per_us = get_cpu_frequency(); -const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); +static const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); int clockSubsecondPrecision() { return s_clock_subsecond_precision; } From 4f06c2ec6f1142f34406aa4d023daa126b1fc89e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 14 Apr 2021 17:49:00 +0200 Subject: [PATCH 034/683] [core] Fixed srt_getsockopt cast for bool options (#1925) Cast to bool instead of int in srt_getsockopt(..) for: - SRTO_SENDER - SRTO_TSBPDMODE - SRTO_DRIFTTRACER - SRTO_ENFORCEDENCRYPTION Additionally: - Fixed TestEnforcedEncryption: bool optval - Minor improvements to Travis run_codecov --- .travis.yml | 2 +- srtcore/core.cpp | 16 ++++++++-------- test/test_enforced_encryption.cpp | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2e2205c0..334677592 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,9 +88,9 @@ script: fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; - source ./scripts/collect-gcov.sh; fi - if (( "$RUN_CODECOV" )); then + source ./scripts/collect-gcov.sh; bash <(curl -s https://codecov.io/bash); fi - if (( "$RUN_SONARCUBE" )); then diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cb71cfb6d..2dfdc9271 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -582,13 +582,13 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_SENDER: - *(int32_t *)optval = m_config.bDataSender; - optlen = sizeof(int32_t); + *(bool *)optval = m_config.bDataSender; + optlen = sizeof(bool); break; case SRTO_TSBPDMODE: - *(int32_t *)optval = m_config.bTSBPD; - optlen = sizeof(int32_t); + *(bool *)optval = m_config.bTSBPD; + optlen = sizeof(bool); break; case SRTO_LATENCY: @@ -679,8 +679,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_DRIFTTRACER: - *(int*)optval = m_config.bDriftTracer; - optlen = sizeof(int); + *(bool*)optval = m_config.bDriftTracer; + optlen = sizeof(bool); break; case SRTO_MINVERSION: @@ -734,8 +734,8 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) #endif case SRTO_ENFORCEDENCRYPTION: - optlen = sizeof(int32_t); // also with TSBPDMODE and SENDER - *(int32_t *)optval = m_config.bEnforcedEnc; + optlen = sizeof(bool); + *(bool *)optval = m_config.bEnforcedEnc; break; case SRTO_IPV6ONLY: diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index cdb0ac719..48cb55f55 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -277,10 +277,10 @@ class TestEnforcedEncryption bool GetEnforcedEncryption(PEER_TYPE peer_type) { const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; - int value = -1; - int value_len = sizeof value; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&value, &value_len), SRT_SUCCESS); - return value ? true : false; + bool optval; + int optlen = sizeof optval; + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS); + return optval ? true : false; } @@ -503,8 +503,8 @@ class TestEnforcedEncryption int m_pollid = 0; - const int s_yes = 1; - const int s_no = 0; + const bool s_yes = true; + const bool s_no = false; const bool m_is_tracing = false; static const char* m_km_state[]; From 8c8d6fb083bf66bb8a6c96fde756f5920656a55d Mon Sep 17 00:00:00 2001 From: quink-black Date: Thu, 15 Apr 2021 15:26:32 +0800 Subject: [PATCH 035/683] [core] Fix build with mbedtls older than 428cc52a73. (#1945) Ref. https://github.com/ARMmbed/mbedtls/issues/1215 --- haicrypt/cryspr-mbedtls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haicrypt/cryspr-mbedtls.h b/haicrypt/cryspr-mbedtls.h index aa10da874..9cc5c6e44 100644 --- a/haicrypt/cryspr-mbedtls.h +++ b/haicrypt/cryspr-mbedtls.h @@ -55,7 +55,7 @@ This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK It is set from hte keystring through CRYSPR_methods.aes_set_key and passed to CRYSPR_methods.aes_XXX. */ -typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ +typedef mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */ struct tag_CRYSPR_methods *crysprMbedtls(void); From f0533946a9b8cabeaa8655ef3df0b45536df8c05 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 14 Apr 2021 12:18:21 +0200 Subject: [PATCH 036/683] [core] SRTO_CONGESTION: Check optlen in getsockopt --- srtcore/core.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2dfdc9271..09e8a2da5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -697,6 +697,9 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_CONGESTION: + if (size_t(optlen) < m_config.sCongestion.size() + 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + strcpy((char *)optval, m_config.sCongestion.c_str()); optlen = (int) m_config.sCongestion.size(); break; From aa51e2d1a7ae124b336534658de52bbd3b836d90 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 10:02:42 +0200 Subject: [PATCH 037/683] [core] Apply PRE sockopt restriction in listening state (#1939) * Docs: clarified the description of sockopt restrictions. * Fixed FEC unit tests: call srt_setsockopt before srt_listen --- docs/API/API-socket-options.md | 11 ++--- srtcore/core.cpp | 2 +- test/test_fec_rebuilding.cpp | 78 +++++++++++++++++++--------------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index c1d4420e1..6c274666a 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -122,13 +122,14 @@ of SRT ever created and put into use. 2. **Restrict**: Defines restrictions on setting the option. The field is empty if the option is not settable (see **Dir** column): - - `pre-bind`: The option cannot be altered on a socket that is already bound (by calling + - `pre-bind`: The option cannot be altered on a socket that is already bound (by calling `srt_bind()` or any other function doing this, including automatic binding when trying to -connect, as well as accepted sockets). +connect, as well as accepted sockets). In other words, once an SRT socket has transitioned from +`SRTS_INIT` to `SRTS_OPENED` socket state. - - `pre`: Like pre-bind, but only for a connected socket (including an accepted socket). If -an option was set on a listener socket, it will be set the same on a socket returned by -`srt_accept()`. + - `pre`: The option cannot be altered on a socket that is in `SRTS_LISTENING`, `SRTS_CONNECTING` +or `SRTS_CONNECTED` state. If an option was set on a listener socket, it will be inherited +by a socket returned by `srt_accept()` (except for `SRTO_STREAMID`). - `post`: The option is unrestricted and can be altered at any time, including when the socket is connected, as well as on an accepted socket. The setting of this flag on a listening diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 09e8a2da5..82bee7857 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -359,7 +359,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) if (IsSet(oflags, SRTO_R_PREBIND) && m_bOpened) throw CUDTException(MJ_NOTSUP, MN_ISBOUND, 0); - if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting)) + if (IsSet(oflags, SRTO_R_PRE) && (m_bConnected || m_bConnecting || m_bListening)) throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); // Option execution. If this returns -1, there's no such option. diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index e289b384b..913473af7 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -287,14 +287,15 @@ TEST(TestFEC, Connection) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,cols:10,arq:never"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,cols:10,arq:never"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + + srt_listen(l, 1); auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); @@ -339,15 +340,16 @@ TEST(TestFEC, ConnectionReorder) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,rows:10,cols:10"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,rows:10,cols:10"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -391,15 +393,16 @@ TEST(TestFEC, ConnectionFull1) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even"; - char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never"; - char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even"; + const char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even"; + const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never"; + const char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -442,15 +445,16 @@ TEST(TestFEC, ConnectionFull2) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even"; - char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always"; - char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even"; + const char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even"; + const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always"; + const char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -494,15 +498,16 @@ TEST(TestFEC, ConnectionMess) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:,cols:10"; - char fec_config2 [] = "fec,cols:,rows:10"; - char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; + const char fec_config1 [] = "fec,cols:,cols:10"; + const char fec_config2 [] = "fec,cols:,rows:10"; + const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -546,13 +551,14 @@ TEST(TestFEC, ConnectionForced) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,rows:20,cols:20"; - char fec_config_final [] = "fec,cols:20,rows:20"; + const char fec_config1 [] = "fec,rows:20,cols:20"; + const char fec_config_final [] = "fec,cols:20,rows:20"; ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -592,14 +598,15 @@ TEST(TestFEC, RejectionConflict) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,cols:10,rows:10"; - char fec_config2 [] = "fec,cols:20,arq:never"; + const char fec_config1 [] = "fec,cols:10,rows:10"; + const char fec_config2 [] = "fec,cols:20,arq:never"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -634,12 +641,12 @@ TEST(TestFEC, RejectionIncompleteEmpty) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - - char fec_config1 [] = "fec,rows:10"; + const char fec_config1 [] = "fec,rows:10"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -675,14 +682,15 @@ TEST(TestFEC, RejectionIncomplete) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_bind(l, (sockaddr*)& sa, sizeof(sa)); - srt_listen(l, 1); - char fec_config1 [] = "fec,rows:10"; - char fec_config2 [] = "fec,arq:never"; + const char fec_config1 [] = "fec,rows:10"; + const char fec_config2 [] = "fec,arq:never"; srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1); srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1); + srt_listen(l, 1); + auto connect_res = std::async(std::launch::async, [&s, &sa]() { return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); @@ -740,7 +748,7 @@ TEST_F(TestFECRebuilding, NoRebuild) SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); // Use the sequence number of the last packet, as usual. - bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); + const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); ASSERT_EQ(have_fec_ctl, true); // By having all packets and FEC CTL packet, now stuff in @@ -817,7 +825,7 @@ TEST_F(TestFECRebuilding, Rebuild) SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); // Use the sequence number of the last packet, as usual. - bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); + const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); ASSERT_EQ(have_fec_ctl, true); // By having all packets and FEC CTL packet, now stuff in @@ -863,7 +871,7 @@ TEST_F(TestFECRebuilding, Rebuild) // And now receive the FEC control packet - bool want_passthru_fec = fec->receive(*fecpkt, loss); + const bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up EXPECT_EQ(loss.size(), 0); From b9d568e05705c38b6a76a088a04f8453bdf56cc6 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 15 Apr 2021 10:43:38 +0200 Subject: [PATCH 038/683] [core] Added more logs around accept errors (#1883) --- srtcore/api.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 3b8f81ef1..ec60e2389 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1067,16 +1067,25 @@ SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64 SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) { if (pw_addr && !pw_addrlen) + { + LOGC(cnlog.Error, log << "srt_accept: provided address, but address length parameter is missing"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } CUDTSocket* ls = locateSocket(listen); if (ls == NULL) + { + LOGC(cnlog.Error, log << "srt_accept: invalid listener socket ID value: " << listen); throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + } // the "listen" socket must be in LISTENING status if (ls->m_Status != SRTS_LISTENING) + { + LOGC(cnlog.Error, log << "srt_accept: socket @" << listen << " is not in listening state (forgot srt_listen?)"); throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); + } // no "accept" in rendezvous connection setup if (ls->m_pUDT->m_config.bRendezvous) @@ -1125,15 +1134,22 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ { // non-blocking receiving, no connection available if (!ls->m_pUDT->m_config.bSynRecving) + { + LOGC(cnlog.Error, log << "srt_accept: no pending connection available at the moment"); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + LOGC(cnlog.Error, log << "srt_accept: listener socket @" << listen << " is already closed"); // listening socket is closed throw CUDTException(MJ_SETUP, MN_CLOSED, 0); } CUDTSocket* s = locateSocket(u); if (s == NULL) + { + LOGC(cnlog.Error, log << "srt_accept: pending connection has unexpectedly closed"); throw CUDTException(MJ_SETUP, MN_CLOSED, 0); + } // Set properly the SRTO_GROUPCONNECT flag s->core().m_config.iGroupConnect = 0; @@ -1166,7 +1182,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ #endif ScopedLock cg(s->m_ControlLock); - + if (pw_addr != NULL && pw_addrlen != NULL) { // Check if the length of the buffer to fill the name in From 944287050f58a7901d485c21f75bf636742f3fdd Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 10:36:23 +0200 Subject: [PATCH 039/683] [core] Fixed getting SRTO_TLPKTDROP: return config value until connected. --- srtcore/core.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 82bee7857..555cc85df 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -610,7 +610,11 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_TLPKTDROP: - *(bool *)optval = m_bTLPktDrop; + if (m_bConnected) + *(bool *)optval = m_bTLPktDrop; + else + *(bool *)optval = m_config.bTLPktDrop; + optlen = sizeof(bool); break; From 35fb87ffffd8a60918644488c122cd9bf41730f1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 15 Apr 2021 18:31:22 +0200 Subject: [PATCH 040/683] [core] const SrtOptionAction (#1942) --- srtcore/core.cpp | 21 ++++++++++++++------- srtcore/sync_posix.cpp | 6 +++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 555cc85df..9b93fab79 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -130,11 +130,15 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_LOSSMAXTTL }; -static const int32_t +const int32_t SRTO_R_PREBIND = BIT(0), //< cannot be modified after srt_bind() SRTO_R_PRE = BIT(1), //< cannot be modified after connection is established SRTO_POST_SPEC = BIT(2); //< executes some action after setting the option + +namespace srt +{ + struct SrtOptionAction { int flags[SRTO_E_SIZE]; @@ -210,8 +214,11 @@ struct SrtOptionAction // passed to a setting function. private_default[SRTO_STREAMID] = string(); } -} -srt_options_action; +}; + +const SrtOptionAction s_sockopt_action; + +} // namespace srt void CUDT::construct() @@ -297,9 +304,9 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) m_config = ancestor.m_config; // Reset values that shall not be derived to default ones. // These declarations should be consistent with SRTO_R_PRIVATE flag. - for (size_t i = 0; i < Size(srt_options_action.flags); ++i) + for (size_t i = 0; i < Size(s_sockopt_action.flags); ++i) { - string* pdef = map_getp(srt_options_action.private_default, SRT_SOCKOPT(i)); + const string* pdef = map_getp(s_sockopt_action.private_default, SRT_SOCKOPT(i)); if (pdef) { try @@ -342,12 +349,12 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - // Match check (confirm optName as index for srt_options_action) + // Match check (confirm optName as index for s_sockopt_action) if (int(optName) < 0 || int(optName) >= int(SRTO_E_SIZE)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Restriction check - const int oflags = srt_options_action.flags[optName]; + const int oflags = s_sockopt_action.flags[optName]; ScopedLock cg (m_ConnectionLock); ScopedLock sendguard (m_SendLock); diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 997cf534c..0972dd731 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -38,7 +38,7 @@ namespace srt namespace sync { -void rdtsc(uint64_t& x) +static void rdtsc(uint64_t& x) { #if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA32_RDTSC uint32_t lval, hval; @@ -123,9 +123,9 @@ static int count_subsecond_precision(int64_t ticks_per_us) return signs; } -static const int64_t s_clock_ticks_per_us = get_cpu_frequency(); +const int64_t s_clock_ticks_per_us = get_cpu_frequency(); -static const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); +const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us); int clockSubsecondPrecision() { return s_clock_subsecond_precision; } From 07320ab6f7f3a2166d95a8cab71435f2c9eaddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Apr 2021 10:02:22 +0200 Subject: [PATCH 041/683] [apps] Option utility fix: handle properly single dash as option value --- apps/apputil.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0afe40cd5..f772beadf 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -205,7 +205,10 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector isoption = true; // If a[0] isn't NUL - because it is dash - then // we can safely check a[1]. - if (a[1] && isdigit(a[1])) + // An expression starting with a dash is not + // an option marker if it is a single dash or + // a negative number. + if (!a[1] || isdigit(a[1])) isoption = false; } From 97efa1b959ce1623f4932fac06d4b94cd8adb3a7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 16 Apr 2021 14:55:22 +0200 Subject: [PATCH 042/683] [docs] Fixed restrictionf for SRTO_MSS and SRTO_LOSSMAXTTL (#1954) - SRTO_MSS: 'pre-bind' restriction instead of pre - SRTO_LOSSMAXTTL has 'post' restriction --- docs/API/API-socket-options.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 6c274666a..4d1565c5c 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -216,12 +216,12 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | | [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | | [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [`SRTO_MSS`](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | | [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | | [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | @@ -733,7 +733,7 @@ Linger time on close (see [SO\_LINGER](http://man7.org/linux/man-pages/man7/sock | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_LOSSMAXTTL` | 1.2.0 | pre | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| `SRTO_LOSSMAXTTL` | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | The value up to which the *Reorder Tolerance* may grow. The *Reorder Tolerance* is the number of packets that must follow the experienced "gap" in sequence numbers @@ -833,7 +833,7 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | Maximum Segment Size. Used for buffer allocation and rate calculation using packet counter assuming fully filled packets. Each party can set its own MSS From 8608ad26ecb095bfb5159530e7f15334c7771a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Apr 2021 13:07:14 +0200 Subject: [PATCH 043/683] [core] Removed logging in cleanup --- srtcore/api.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ec60e2389..71e4a8772 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -294,6 +294,14 @@ int CUDTUnited::startup() int CUDTUnited::cleanup() { + // IMPORTANT!!! + // In this function there must be NO LOGGING AT ALL. This function may + // potentially be called from within the global program destructor, and + // therefore some of the facilities used by the logging system - including + // the default std::cerr object bound to it by default, but also a different + // stream that the user's app has bound to it, and which got destroyed + // together with already exited main() - may be already deleted when + // executing this procedure. ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) @@ -303,7 +311,6 @@ int CUDTUnited::cleanup() return 0; m_bClosing = true; - HLOGC(inlog.Debug, log << "GarbageCollector: thread EXIT"); // NOTE: we can do relaxed signaling here because // waiting on m_GCStopCond has a 1-second timeout, // after which the m_bClosing flag is cheched, which From 43b78eb6c3a4efe0ba49e808dadc50775dc67918 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 16 Apr 2021 16:18:41 +0200 Subject: [PATCH 044/683] [docs] API-functions: pass optlen in srt_getsockopt (#1941) Further corrections, including C string handling. --- docs/API/API-functions.md | 51 +++++++++++++++++++--------------- docs/API/API-socket-options.md | 31 +++++++++++---------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 0e65d5ab3..cf800413f 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1558,24 +1558,29 @@ port number after it has been autoselected. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- - + ### srt_getsockopt ### srt_getsockflag -``` + +```c++ int srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); ``` -Gets the value of the given socket option (from a socket or a group). +Gets the value of the given socket option (from a socket or a group). -The first version ([`srt_getsockopt`](#srt_getsockopt)) respects the BSD socket -API convention, although the "level" parameter is ignored. The second version +The first version ([`srt_getsockopt`](#srt_getsockopt)) follows the BSD socket +API convention, although the "level" parameter is ignored. The second version ([`srt_getsockflag`](#srt_getsockflag)) omits the "level" parameter completely. -Options correspond to various data types, so you need to know what data type is -assigned to a particular option, and to pass a variable of the appropriate data -type. Specifications are provided in the `apps/socketoptions.hpp` file at the -`srt_options` object declaration. +Options correspond to various data types (see [API-socket-options.md](./API-socket-options.md)). +A variable `optval` of the appropriate data type has to be passed. +The integer value of `optlen` should originally contain the size of the `optval` type provided; +on return, it will be set to the size of the value returned. +For most options, it will be the size of an integer. Some options, however, use types `bool`, `int64_t`, `C string`, etc. +(see [API-socket-options.md](./API-socket-options.md#sockopt_types)). + +The application is responsible for allocating sufficient memory space as defined and pointed to by `optval`. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -1586,7 +1591,7 @@ type. Specifications are provided in the `apps/socketoptions.hpp` file at the |:-------------------------------- |:---------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | | [`SRT_EINVOP`](#srt_einvop) | Option `opt` indicates no valid option | -| | | +| | | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1595,19 +1600,19 @@ type. Specifications are provided in the `apps/socketoptions.hpp` file at the ### srt_setsockopt ### srt_setsockflag -``` +```c++ int srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); ``` -Sets a value for a socket option in the socket or group. +Sets a value for a socket option in the socket or group. -The first version ([`srt_setsockopt`](#srt_setsockopt)) respects the BSD socket -API convention, although the "level" parameter is ignored. The second version +The first version ([`srt_setsockopt`](#srt_setsockopt)) follows the BSD socket +API convention, although the "level" parameter is ignored. The second version ([`srt_setsockflag`](#srt_setsockflag)) omits the "level" parameter completely. -Options correspond to various data types, so you need to know what data type is -assigned to a particular option, and to pass a variable of the appropriate data +Options correspond to various data types, so you need to know what data type is +assigned to a particular option, and to pass a variable of the appropriate data type with the option value to be set. Please note that some of the options can only be set on sockets or only on @@ -1619,14 +1624,16 @@ are then derived by the member sockets. | `SRT_ERROR` | (-1) in case of error, otherwise 0 | | | | -| Errors | | -|:------------------------------- |:--------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | -| [`SRT_EINVOP`](#srt_einvop) | Option `opt` indicates no valid option | +| Errors | | +|:----------------------------------- |:--------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EINVPARAM`](#srt_einvparam) | Option `opt` indicates no valid option | +| [`SRT_EBOUNDSOCK`](#srt_eboundsock) | Tried to set an option with PRE_BIND restriction on a bound socket. | +| [`SRT_ECONNSOCK`](#srt_econnsock) | Tried to set an option with PRE_BIND or PRE restriction on a socket in connecting/listening/connected state. | | | | -**NOTE*: Various other errors may result from problems when setting a -specific option (see option description for details). +**NOTE*: Various other errors may result from problems when setting a +specific option (see option description in [API-socket-options.md](./API-socket-options.md) for details). [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 4d1565c5c..9a3342f7b 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -7,31 +7,32 @@ to the system `setsockopt/getsockopt` functions. - [Getting and Setting Options](#getting-and-setting-options) - [List of Options](#list-of-options) -## Types Used in Socket Options +## Types Used in Socket Options Possible types of socket options are: -- `int32_t` - This type can usually be treated as an `int` equivalent since it -does not change size on 64-bit systems. For clarity, options use this fixed size -integer. In some cases the value is expressed using an enumeration type (see below). +- `int32_t` - a 32-bit integer. On most systems similar to `int`. +In some cases the value is expressed using an enumeration type +(see [Enumeration types...](#enumeration_types) section below). -- `int64_t` - Some options need the parameter specified as 64-bit integer. +- `int64_t` - a 64-bit integer. -- `bool` - Requires the use of a boolean type (`` for C, or built-in +- `bool` - a Boolean type (`` for C, or built-in for C++). When *setting* an option, passing the value through an `int` type is -also properly recognized. When *getting* an option, however, you should use the -`bool` type, although you can risk passing a variable of `int` type initialized -with 0 and then checking if the resulting value is equal to 0 (just don't compare -the result with 1). +also properly recognized. When *getting* an option, however, the`bool` type +should be used. It is also possible to pass a variable of `int` type initialized +with 0 and then comparing the resulting value with 0 (just don't compare +the result with 1 or `true`). -- `string` - When *setting* an option, pass the character array pointer as value -and the string length as length. When *getting*, pass an array of sufficient size -(as specified in the size variable). Every option with this type that can be -read should specify the maximum length of that array. +- `string` - a C string. When *setting* an option, a `const char*` character array pointer +is expected to be passed in `optval` and the array length in `optlen` **without the terminating NULL character**. +When *getting*, an array is expected to be passed in `optval` with a +sufficient size **with an extra space for the terminating NULL character** provided in `optlen`. +The return value of `optlen` does not count the terminating NULL character. - `linger` - Linger structure. Used exclusively with `SRTO_LINGER`. -### Enumeration Types Used in Options +### Enumeration Types Used in Options #### `SRT_TRANSTYPE` From 3bf5ceb30261f8b5b1676a8972068659f549fc4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 19 Apr 2021 10:48:10 +0200 Subject: [PATCH 045/683] [core] Check invalid sockopt values (#1956) * SRTO_CONNTIMEO: reject negative * SRTO_PAYLOADSIZE: reject negative and fix FEC * SRTO_SNDDROPDELAY >= -1 * SRTO_SNDTIMEO >= -1 * SRTO_*LATENCY >= 0 * SRTO_PEERIDLETIMEO >= 0 * SRTO_RCVTIMEO >= -1 --- docs/API/API-socket-options.md | 34 +++++++++--------- srtcore/socketconfig.h | 63 ++++++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 9a3342f7b..7f0316a6e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -198,7 +198,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | | :----------------------------------------------------- | :---: | :------: | :-------: | :-----: | :---------------: | :------: |:---:|:-----:| | [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | * | W | S | +| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | \* | W | S | | [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | | [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | | [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | @@ -212,49 +212,49 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | | [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | | [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. * | RW | GSD | +| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. \* | RW | GSD | | [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | | [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | +| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 \* | 0.. | RW | GSD | | [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | on, 180 | 0.. | RW | GSD | | [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | | [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | | [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | * | RW | GSD | +| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | \* | RW | GSD | | [`SRTO_MSS`](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | * | | RW | GSD+ | +| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | \* | | RW | GSD+ | | [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | | [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | | [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..79] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | * | RW | GSD | +| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | +| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | \* | RW | GSD | | [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | | [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | | [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | | [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | | [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | * | 0.. | RW | GSD | +| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | \* | 0.. | RW | GSD | | [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | | [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | | [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | | [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | | [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | +| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | | [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | * | -1.. | W | GSD+ | +| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | \* | -1.. | W | GSD+ | | [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | | [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | | [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | | [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | * | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | * | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | * | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | * | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | * | RW | GSD+ | +| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | \* | | RW | GSD | +| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | \* | W | S | +| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | \* | | W | S | +| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | +| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | \* | RW | GSD+ | | [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions @@ -997,7 +997,7 @@ encrypted connection, they have to simply set the same passphrase. | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | \* | W | GSD | +| `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | Sets the maximum declared size of a single call to sending function in Live mode. When set to 0, there's no limit for a single sending call. diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 969d624e5..c63a41abb 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -502,7 +502,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iSndTimeOut = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndTimeOut = val; } }; @@ -511,7 +515,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvTimeOut = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvTimeOut = val; } }; @@ -660,8 +668,12 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvLatency = cast_optval(optval, optlen); - co.iPeerLatency = cast_optval(optval); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + co.iPeerLatency = val; } }; template<> @@ -669,7 +681,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRcvLatency = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; } }; template<> @@ -677,7 +693,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iPeerLatency = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerLatency = val; } }; template<> @@ -693,9 +713,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - // Surprise: you may be connected to alter this option. - // The application may manipulate this option on sender while transmitting. - co.iSndDropDelay = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndDropDelay = val; } }; template<> @@ -803,8 +825,12 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + using namespace srt::sync; - co.tdConnTimeOut = milliseconds_from(cast_optval(optval, optlen)); + co.tdConnTimeOut = milliseconds_from(val); } }; @@ -894,8 +920,13 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { using namespace srt_logging; + const int val = cast_optval(optval, optlen); + if (val < 0) + { + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } - if (*(int *)optval > SRT_LIVE_MAX_PLSIZE) + if (val > SRT_LIVE_MAX_PLSIZE) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -915,7 +946,7 @@ struct CSrtConfigSetter } size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.zExpPayloadSize > efc_max_payload_size) + if (val > efc_max_payload_size) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size @@ -924,7 +955,7 @@ struct CSrtConfigSetter } } - co.zExpPayloadSize = cast_optval(optval, optlen); + co.zExpPayloadSize = val; } }; @@ -1072,7 +1103,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iPeerIdleTimeout = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerIdleTimeout = val; } }; From 90c62af4e51b1098bbfd06cee8c362bdcbb80431 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Apr 2021 17:12:25 +0200 Subject: [PATCH 046/683] [core] Fixed type conversion build warnings --- apps/srt-tunnel.cpp | 4 ++-- srtcore/buffer.cpp | 4 ++-- srtcore/channel.cpp | 6 +++++- srtcore/core.cpp | 18 +++++++++--------- srtcore/core.h | 16 ++++------------ srtcore/group.cpp | 14 +++++++------- srtcore/logging.h | 3 +++ srtcore/socketconfig.h | 6 +++--- srtcore/window.h | 2 +- 9 files changed, 36 insertions(+), 37 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 0567b5764..e5175ce8b 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -701,10 +701,10 @@ unique_ptr TcpMedium::Accept() // Configure 1s timeout timeval timeout_1s { 1, 0 }; - int st = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s); + int st SRT_ATR_UNUSED = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s); timeval re; socklen_t size = sizeof re; - int st2 = getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&re, &size); + int st2 SRT_ATR_UNUSED = getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&re, &size); LOGP(applog.Debug, "Setting SO_RCVTIMEO to @", m_socket, ": ", st == -1 ? "FAILED" : "SUCCEEDED", ", read-back value: ", st2 == -1 ? int64_t(-1) : (int64_t(re.tv_sec)*1000000 + re.tv_usec)/1000, "ms"); diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 5ca443485..20f5da3d8 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1768,8 +1768,8 @@ steady_clock::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t timestamp_us) { carryover = int64_t(CPacket::MAX_TIMESTAMP) + 1; } - // - else if ((timestamp_us >= TSBPD_WRAP_PERIOD) && (timestamp_us <= (TSBPD_WRAP_PERIOD * 2))) + // timestamp_us >= TSBPD_WRAP_PERIOD + else if (timestamp_us <= (TSBPD_WRAP_PERIOD * 2)) { /* Exiting wrap check period (if for packet delivery head) */ m_bTsbPdWrapCheck = false; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index c35510747..865eaaabc 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -183,8 +183,9 @@ void CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, + const int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); +#if ENABLE_LOGGING if (res == -1) { int err = errno; @@ -192,6 +193,7 @@ void CChannel::createSocket(int family) LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " << m_mcfg.iIpV6Only << ": " << SysStrError(err, msg, 159)); } +#endif // ENABLE_LOGGING } } @@ -352,9 +354,11 @@ void CChannel::setUDPSockOpt() if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) { +#if ENABLE_LOGGING char buf[255]; const char* err = SysStrError(NET_ERROR, buf, 255); LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); +#endif // ENABLE_LOGGING throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9b93fab79..cefb716c5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -313,7 +313,7 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) { // Ignore errors here - this is a development-time granted // value, not user-provided value. - m_config.set(SRT_SOCKOPT(i), pdef->data(), pdef->size()); + m_config.set(SRT_SOCKOPT(i), pdef->data(), (int) pdef->size()); } catch (...) { @@ -6567,7 +6567,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING - double bw = Bps2Mbps( m_iBandwidth * m_iMaxSRTPayloadSize ); + double bw = Bps2Mbps(int64_t(m_iBandwidth) * m_iMaxSRTPayloadSize ); HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)"); #endif } @@ -7024,10 +7024,10 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; - double interval = count_microseconds(currtime - m_stats.tsLastSampleTime); + const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; - perf->usPktSndPeriod = count_microseconds(m_tdSendInterval); + perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval); perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); @@ -7172,9 +7172,9 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - if SRTO_MAXBW == 0, use SRTO_INPUTBW + SRTO_OHEADBW // - if SRTO_INPUTBW == 0, pass 0 to requst in-buffer sampling // Bytes/s - int bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW - m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW - 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling + const int64_t bw = m_config.llMaxBW != 0 ? m_config.llMaxBW : // When used SRTO_MAXBW + m_config.llInputBW != 0 ? withOverhead(m_config.llInputBW) : // SRTO_INPUTBW + SRT_OHEADBW + 0; // When both MAXBW and INPUTBW are 0, request in-buffer sampling // Note: setting bw == 0 uses BW_INFINITE value in LiveCC m_CongCtl->updateBandwidth(m_config.llMaxBW, bw); @@ -9408,12 +9408,12 @@ int CUDT::processData(CUnit* in_unit) { IF_HEAVY_LOGGING(exc_type = "BELATED"); steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - long bltime = CountIIR( + const double bltime = (double) CountIIR( uint64_t(m_stats.traceBelatedTime) * 1000, count_microseconds(steady_clock::now() - tsbpdtime), 0.2); enterCS(m_StatsLock); - m_stats.traceBelatedTime = double(bltime) / 1000.0; + m_stats.traceBelatedTime = bltime / 1000.0; m_stats.traceRcvBelated++; leaveCS(m_StatsLock); HLOGC(qrlog.Debug, diff --git a/srtcore/core.h b/srtcore/core.h index 70dbb2f7d..b3498c814 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -364,7 +364,7 @@ class CUDT int minSndSize(int len = 0) const { - const int ps = maxPayloadSize(); + const int ps = (int) maxPayloadSize(); if (len == 0) // wierd, can't use non-static data member as default argument! len = ps; return m_config.bMessageAPI ? (len+ps-1)/ps : 1; @@ -379,7 +379,7 @@ class CUDT // So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1) // XXX Would be nice to check if local_time > m_tsStartTime, // otherwise it may go unnoticed with clock skew. - return srt::sync::count_microseconds(from_time - m_stats.tsStartTime); + return (int32_t) srt::sync::count_microseconds(from_time - m_stats.tsStartTime); } void setPacketTS(CPacket& p, const time_point& local_time) @@ -400,18 +400,10 @@ class CUDT { using namespace srt::sync; // Random Initial Sequence Number (normal mode) - srand(count_microseconds(steady_clock::now().time_since_epoch())); + srand((unsigned) count_microseconds(steady_clock::now().time_since_epoch())); return (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); } - // XXX See CUDT::tsbpd() to see how to implement it. This should - // do the same as TLPKTDROP feature when skipping packets that are agreed - // to be lost. Note that this is predicted to be called with TSBPD off. - // This is to be exposed for the application so that it can require this - // sequence to be skipped, if that packet has been otherwise arrived through - // a different channel. - void skipIncoming(int32_t seq); - // For SRT_tsbpdLoop static CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop std::set& pollset() { return m_sPollID; } @@ -669,7 +661,7 @@ class CUDT int sndSpaceLeft() { - return sndBuffersLeft() * maxPayloadSize(); + return static_cast(sndBuffersLeft() * maxPayloadSize()); } int sndBuffersLeft() diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 4c8feff31..032ac1580 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -147,8 +147,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // time when the connection process is done, until the first reading/writing happens. ScopedLock cg(m_GroupLock); - SRTSOCKET mpeer; - steady_clock::time_point start_time; + IF_LOGGING(SRTSOCKET mpeer); + IF_LOGGING(steady_clock::time_point start_time); bool found = false; @@ -159,8 +159,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // Found it. Get the socket's peer's ID and this socket's // Start Time. Once it's delivered, this can be used to calculate // the Master-to-Slave start time difference. - mpeer = gi->ps->m_PeerID; - start_time = gi->ps->core().socketStartTime(); + IF_LOGGING(mpeer = gi->ps->m_PeerID); + IF_LOGGING(start_time = gi->ps->core().socketStartTime()); HLOGC(gmlog.Debug, log << "getMasterData: found RUNNING master @" << gi->id << " - reporting master's peer $" << mpeer << " starting at " << FormatTime(start_time)); @@ -185,8 +185,8 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) // Found it. Get the socket's peer's ID and this socket's // Start Time. Once it's delivered, this can be used to calculate // the Master-to-Slave start time difference. - mpeer = gi->ps->core().m_PeerID; - start_time = gi->ps->core().socketStartTime(); + IF_LOGGING(mpeer = gi->ps->core().m_PeerID); + IF_LOGGING(start_time = gi->ps->core().socketStartTime()); HLOGC(gmlog.Debug, log << "getMasterData: found IDLE/PENDING master @" << gi->id << " - reporting master's peer $" << mpeer << " starting at " << FormatTime(start_time)); @@ -203,7 +203,7 @@ void CUDTGroup::debugMasterData(SRTSOCKET slave) { // The returned master_st is the master's start time. Calculate the // differene time. - steady_clock::duration master_tdiff = m_tsStartTime - start_time; + IF_LOGGING(steady_clock::duration master_tdiff = m_tsStartTime - start_time); LOGC(cnlog.Debug, log << CONID() << "FOUND GROUP MASTER LINK: peer=$" << mpeer << " - start time diff: " << FormatDuration(master_tdiff)); } diff --git a/srtcore/logging.h b/srtcore/logging.h index bfdfb375e..cfb239f1e 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -66,6 +66,8 @@ written by // Usage: LOGP(gglog.Debug, param1, param2, param3); #define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__) +#define IF_LOGGING(instr) instr + #if ENABLE_HEAVY_LOGGING #define HLOGC LOGC @@ -95,6 +97,7 @@ written by #define HLOGP(...) #define IF_HEAVY_LOGGING(instr) (void)0 +#define IF_LOGGING(instr) (void)0 #endif diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index c63a41abb..6b8a9325e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -146,7 +146,7 @@ class StringStorage memcpy(stor, s, length); stor[length] = 0; - len = length; + len = (int) length; return true; } @@ -945,8 +945,8 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (val > efc_max_payload_size) + const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (size_t(val) > efc_max_payload_size) { LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size diff --git a/srtcore/window.h b/srtcore/window.h index b7f510163..7dbfb73bf 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -218,7 +218,7 @@ class CPktTimeWindow: CPktTimeWindowTools m_tsCurrArrTime = srt::sync::steady_clock::now(); // record the packet interval between the current and the last one - m_aPktWindow[m_iPktWindowPtr] = srt::sync::count_microseconds(m_tsCurrArrTime - m_tsLastArrTime); + m_aPktWindow[m_iPktWindowPtr] = (int) srt::sync::count_microseconds(m_tsCurrArrTime - m_tsLastArrTime); m_aBytesWindow[m_iPktWindowPtr] = pktsz; // the window is logically circular From a95e5aee722e64ddc0f00762aaca0721728bbd13 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Apr 2021 18:04:55 +0200 Subject: [PATCH 047/683] [build] Added -Werror to Travis CI Linux and MacOS jobs --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 334677592..4ffe49add 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,20 +23,24 @@ matrix: - os: linux env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_EXPERIMENTAL_BONDING=ON' + - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_EXPERIMENTAL_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - RUN_SONARCUBE=1 - RUN_CODECOV=1 - env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_LOGGING=OFF -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_EXPERIMENTAL_BONDING=ON' + - BUILD_OPTS='-DENABLE_LOGGING=OFF -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_EXPERIMENTAL_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - os: linux env: BUILD_TYPE=Release - os: osx osx_image: xcode11.1 - env: BUILD_TYPE=Debug + env: + - BUILD_TYPE=Debug + - BUILD_OPTS='-DCMAKE_CXX_FLAGS="-Werror"' - os: osx osx_image: xcode11.1 - env: BUILD_TYPE=Release + env: + - BUILD_TYPE=Release + - BUILD_OPTS='-DCMAKE_CXX_FLAGS="-Werror"' - os: linux compiler: x86_64-w64-mingw32-g++ addons: From 672f0cbfe20efdcf671e356ea2e5d456aa79d09e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 23 Apr 2021 11:05:58 +0200 Subject: [PATCH 048/683] [apps] Fixed virtual call from destructor. Virtual functions should not be invoked from a constructor or destructor of the same class. --- apps/transmitmedia.hpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 604a8dfba..527f005d9 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -56,7 +56,7 @@ class SrtCommon SRTSOCKET Socket() const { return m_sock; } SRTSOCKET Listener() const { return m_bindsock; } - virtual void Close(); + void Close(); protected: @@ -109,7 +109,6 @@ class SrtSource: public Source, public SrtCommon bool IsOpen() override { return IsUsable(); } bool End() override { return IsBroken(); } - void Close() override { return SrtCommon::Close(); } SRTSOCKET GetSRTSocket() const override { @@ -137,7 +136,6 @@ class SrtTarget: public Target, public SrtCommon int Write(const char* data, size_t size, int64_t src_time, ostream &out_stats = cout) override; bool IsOpen() override { return IsUsable(); } bool Broken() override { return IsBroken(); } - void Close() override { return SrtCommon::Close(); } size_t Still() override { @@ -181,18 +179,8 @@ class SrtModel: public SrtCommon string m_host; int m_port = 0; - SrtModel(string host, int port, map par); void Establish(std::string& name); - - void Close() - { - if (m_sock != SRT_INVALID_SOCK) - { - srt_close(m_sock); - m_sock = SRT_INVALID_SOCK; - } - } }; From 5dbceb20055e0575b937073139665571f370a88b Mon Sep 17 00:00:00 2001 From: kkarsten Date: Mon, 26 Apr 2021 10:39:23 +0200 Subject: [PATCH 049/683] [docs] Update srt-live-transmit.md (#1967) A note on passphrase length --- docs/apps/srt-live-transmit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 184bb32a7..287a47649 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -314,7 +314,7 @@ All other parameters are SRT socket options. The following have the following va | `nakreport` | `bool` | `SRTO_NAKREPORT` | Enables/disables periodic NAK reports | | `oheadbw` | 5..100 | `SRTO_OHEADBW` | limits bandwidth overhead, percents | | `packetfilter` | `string` | `SRTO_PACKETFILTER` | Set up the packet filter. | -| `passphrase` | `string` | `SRTO_PASSPHRASE` | Password for the encrypted transmission. | +| `passphrase` | `string` | `SRTO_PASSPHRASE` | Password for the encrypted transmission. (must be 10 to 79 characters) | | `payloadsize` | 0.. | `SRTO_PAYLOADSIZE` | Maximum payload size. | | `pbkeylen` | {16, 24, 32} | `SRTO_PBKEYLEN` | Crypto key length in bytes. | | `peeridletimeo` | `ms` | `SRTO_PEERIDLETIMEO` | Peer idle timeout. | From 291e010fbf91b13b92e1cde5dcb824a9d7f4e353 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Apr 2021 10:44:11 +0200 Subject: [PATCH 050/683] [core] New API function srt_clock_type() (#1887) --- docs/API/API-functions.md | 30 ++++++++++++++++++++++++++++++ srtcore/srt.h | 12 ++++++++++++ srtcore/srt_c_api.cpp | 5 +++++ srtcore/sync.h | 10 ---------- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index cf800413f..44f8ac19a 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -135,6 +135,7 @@ |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | | [srt_time_now](#srt_time_now) | Get time in microseconds elapsed since epoch using SRT internal clock
(steady or monotonic clock) | | [srt_connection_time](#srt_connection_time) | Get connection time in microseconds elapsed since epoch using SRT internal clock
(steady or monotonic clock) | +| [srt_clock_type](#srt_clock_type) | Get the type of clock used internally by SRT | | | |

Diagnostics

@@ -2646,7 +2647,36 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistic --- +### srt_clock_type +```c +int srt_clock_type(void); +``` + +Get the type of clock used internally by SRT to be used only for informtational peurpose. +Using any time source except for [`srt_time_now()`](#srt_time_now) and [`srt_connection_time(SRTSOCKET)`](#srt_connection_time) +to timestamp packets submitted to SRT is not recommended and must be done with awareness and at your own risk. + +| Returns | Clock Type | Description | +| :------ | :---------------------------------- | :------------------------------------------| +| 0 | `SRT_SYNC_CLOCK_STDCXX_STEADY` | C++11 `std::chrono::steady_clock` | +| 1 | `SRT_SYNC_CLOCK_GETTIME_MONOTONIC` | `clock_gettime` with `CLOCK_MONOTONIC` | +| 2 | `SRT_SYNC_CLOCK_WINQPC` | Windows `QueryPerformanceCounter(..)` | +| 3 | `SRT_SYNC_CLOCK_MACH_ABSTIME` | `mach_absolute_time()` | +| 4 | `SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY` | POSIX `gettimeofday(..)` | +| 5 | `SRT_SYNC_CLOCK_AMD64_RDTSC` | `asm("rdtsc" ..)` | +| 6 | `SRT_SYNC_CLOCK_IA32_RDTSC` | `asm volatile("rdtsc" ..)` | +| 7 | `SRT_SYNC_CLOCK_IA64_ITC` | `asm("mov %0=ar.itc" ..)` | + +| Errors | | +|:--------------------------------- |:---------------------------------------------------------- | +| None | | +| | | + + +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- ## Diagnostics diff --git a/srtcore/srt.h b/srtcore/srt.h index f9fa24655..92be107d2 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -995,6 +995,18 @@ SRT_API int64_t srt_time_now(void); SRT_API int64_t srt_connection_time(SRTSOCKET sock); +// Possible internal clock types +#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock +#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC +#define SRT_SYNC_CLOCK_WINQPC 2 +#define SRT_SYNC_CLOCK_MACH_ABSTIME 3 +#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4 +#define SRT_SYNC_CLOCK_AMD64_RDTSC 5 +#define SRT_SYNC_CLOCK_IA32_RDTSC 6 +#define SRT_SYNC_CLOCK_IA64_ITC 7 + +SRT_API int srt_clock_type(void); + #ifdef __cplusplus } #endif diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 1763198ed..bfd130148 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -410,4 +410,9 @@ int64_t srt_connection_time(SRTSOCKET sock) return CUDT::socketStartTime(sock); } +int srt_clock_type() +{ + return SRT_SYNC_CLOCK; +} + } diff --git a/srtcore/sync.h b/srtcore/sync.h index 271b15426..7457c3df9 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -11,16 +11,6 @@ #ifndef INC_SRT_SYNC_H #define INC_SRT_SYNC_H -// Possible internal clock types -#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock -#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC -#define SRT_SYNC_CLOCK_WINQPC 2 -#define SRT_SYNC_CLOCK_MACH_ABSTIME 3 -#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4 -#define SRT_SYNC_CLOCK_AMD64_RDTSC 5 -#define SRT_SYNC_CLOCK_IA32_RDTSC 6 -#define SRT_SYNC_CLOCK_IA64_ITC 7 - #include #include #ifdef ENABLE_STDCXX_SYNC From d898f1ccee4ab53b00101c193d9fd7ea23304575 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Tue, 27 Apr 2021 17:16:18 +0200 Subject: [PATCH 051/683] [core] Improved RTT estimation (#1957) * Receiver: use the first RTT estimation without smoothing. * Sender: take RTT value from ACK in case of unidirectional transmission. --- CMakeLists.txt | 1 + srtcore/core.cpp | 190 ++++++++++++++++++++++++++++++++++++++++++----- srtcore/core.h | 26 ++++--- 3 files changed, 189 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25f34c881..df43a1e26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ endforeach() # SRT_DEBUG_TLPKTDROP_DROPSEQ 1 # SRT_DEBUG_SNDQ_HIGHRATE 1 # SRT_DEBUG_BONDING_STATES 1 +# SRT_DEBUG_RTT 1 /* RTT trace */ # SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */ # option defaults diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cefb716c5..7f23e2653 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -241,7 +241,6 @@ void CUDT::construct() // Will be reset to 0 for HSv5, this value is important for HSv4. m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; - // Initial status m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -932,8 +931,10 @@ void CUDT::open() m_pRNode->m_pPrev = m_pRNode->m_pNext = NULL; m_pRNode->m_bOnList = false; - m_iRTT = 10 * COMM_SYN_INTERVAL_US; - m_iRTTVar = m_iRTT >> 1; + // Set initial values of smoothed RTT and RTT variance. + m_iRTT = INITIAL_RTT; + m_iRTTVar = INITIAL_RTTVAR; + m_bIsFirstRTTReceived = false; // set minimum NAK and EXP timeout to 300ms m_tdMinNakInterval = milliseconds_from(300); @@ -1867,6 +1868,74 @@ void SrtExtractHandshakeExtensions(const char* bufbegin, size_t buflength, } } +#if SRT_DEBUG_RTT +class RttTracer +{ +public: + RttTracer() + { + } + + ~RttTracer() + { + srt::sync::ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(const srt::sync::steady_clock::time_point& currtime, + const std::string& event, int rtt_sample, int rttvar_sample, + bool is_smoothed_rtt_reset, int64_t recvTotal, + int smoothed_rtt, int rttvar) + { + srt::sync::ScopedLock lck(m_mtx); + create_file(); + + m_fout << srt::sync::FormatTimeSys(currtime) << ","; + m_fout << srt::sync::FormatTime(currtime) << ","; + m_fout << event << ","; + m_fout << rtt_sample << ","; + m_fout << rttvar_sample << ","; + m_fout << is_smoothed_rtt_reset << ","; + m_fout << recvTotal << ","; + m_fout << smoothed_rtt << ","; + m_fout << rttvar << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "Timepoint_SYST,Timepoint_STDY,Event,usRTTSample," + "usRTTVarSample,IsSmoothedRTTReset,pktsRecvTotal," + "usSmoothedRTT,usRTTVar\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "rtt_trace_" + str_tnow + "_" + SRT_SYNC_CLOCK_STR + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; +}; + +RttTracer s_rtt_trace; +#endif + bool CUDT::processSrtMsg(const CPacket *ctrlpkt) { @@ -3261,8 +3330,8 @@ void CUDT::synchronizeWithGroup(CUDTGroup* gp) } } } - #endif + void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { ScopedLock cg (m_ConnectionLock); @@ -4485,10 +4554,15 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; - m_iRTTVar = m_iRTT >> 1; + m_iRTTVar = ib.m_iRTT / 2; m_iBandwidth = ib.m_iBandwidth; } +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Connect", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); +#endif + SRT_REJECT_REASON rr = setupCC(); if (rr != SRT_REJ_UNKNOWN) { @@ -5384,10 +5458,15 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, if (m_pCache->lookup(&ib) >= 0) { m_iRTT = ib.m_iRTT; - m_iRTTVar = m_iRTT >> 1; + m_iRTTVar = ib.m_iRTT / 2; m_iBandwidth = ib.m_iBandwidth; } +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Accept", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); +#endif + m_PeerAddr = peer; // This should extract the HSREQ and KMREQ portion in the handshake packet. @@ -5851,6 +5930,11 @@ bool CUDT::closeInternal() ib.m_iBandwidth = m_iBandwidth; m_pCache->update(&ib); +#if SRT_DEBUG_RTT + s_rtt_trace.trace(steady_clock::now(), "Cache", -1, -1, + m_bIsFirstRTTReceived, -1, m_iRTT, -1); +#endif + m_bConnected = false; } @@ -7998,15 +8082,63 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point } // This check covers fields up to ACKD_BUFFERLEFT. - // Update RTT - // m_iRTT = ackdata[ACKD_RTT]; - // m_iRTTVar = ackdata[ACKD_RTTVAR]; - // XXX These ^^^ commented-out were blocked in UDT; - // the current RTT calculations are exactly the same as in UDT4. - const int rtt = ackdata[ACKD_RTT]; + // Extract RTT estimate and RTTVar from the ACK packet. + const int rtt = ackdata[ACKD_RTT]; + const int rttvar = ackdata[ACKD_RTTVAR]; + + // Update the values of smoothed RTT and the variation in RTT samples + // on subsequent RTT estimates extracted from the ACK packets + // (during transmission). + if (m_bIsFirstRTTReceived) + { + // Suppose transmission is bidirectional if sender is also receiving + // data packets. + enterCS(m_StatsLock); + const bool bPktsReceived = m_stats.recvTotal != 0; + leaveCS(m_StatsLock); - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + if (bPktsReceived) // Transmission is bidirectional. + { + // RTT value extracted from the ACK packet (rtt) is already smoothed + // RTT obtained at the receiver side. Apply EWMA anyway for the second + // time on the sender side. Ignore initial values which might arrive + // after the smoothed RTT on the sender side has been + // reset to the very first RTT sample received from the receiver. + // TODO: The case of bidirectional transmission requires further + // improvements and testing. Double smoothing is applied here to be + // consistent with the previous behavior. + + if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + { + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + } + } + else // Transmission is unidirectional. + { + // Simply take the values of smoothed RTT and RTT variance from + // the ACK packet. + m_iRTT = rtt; + m_iRTTVar = rttvar; + } + } + // Reset the value of smoothed RTT to the first real RTT estimate extracted + // from an ACK after initialization (at the beginning of transmission). + // In case of resumed connection over the same network, the very first RTT + // value sent within an ACK will be taken from cache and equal to previous + // connection's final smoothed RTT value. The reception of such a value + // will also trigger the smoothed RTT reset at the sender side. + else if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + { + m_iRTT = rtt; + m_iRTTVar = rttvar; + m_bIsFirstRTTReceived = true; + } + +#if SRT_DEBUG_RTT + s_rtt_trace.trace(currtime, "ACK", rtt, rttvar, m_bIsFirstRTTReceived, + m_stats.recvTotal, m_iRTT, m_iRTTVar); +#endif /* Version-dependent fields: * Original UDT (total size: ACKD_TOTAL_SIZE_SMALL): @@ -8062,7 +8194,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival { int32_t ack = 0; - // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair + // Calculate RTT estimate on the receiver side based on ACK/ACKACK pair. const int rtt = m_ACKWindow.acknowledge(ctrlpkt.getAckSeqNo(), ack, tsArrival); if (rtt == -1) @@ -8091,12 +8223,32 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival return; } - // If increasing delay is detected + // If increasing delay is detected. // sendCtrl(UMSG_CGWARNING); - // Calculate RTT (EWMA) on the receiver side - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + // Update the values of smoothed RTT and the variation in RTT samples + // on subsequent RTT samples (during transmission). + if (m_bIsFirstRTTReceived) + { + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); + m_iRTT = avg_iir<8>(m_iRTT, rtt); + } + // Reset the value of smoothed RTT on the first RTT sample after initialization + // (at the beginning of transmission). + // In case of resumed connection over the same network, the initial RTT + // value will be taken from cache and equal to previous connection's + // final smoothed RTT value. + else + { + m_iRTT = rtt; + m_iRTTVar = rtt / 2; + m_bIsFirstRTTReceived = true; + } + +#if SRT_DEBUG_RTT + s_rtt_trace.trace(tsArrival, "ACKACK", rtt, -1, m_bIsFirstRTTReceived, + -1, m_iRTT, m_iRTTVar); +#endif updateCC(TEV_ACKACK, EventVariant(ack)); diff --git a/srtcore/core.h b/srtcore/core.h index b3498c814..a89c23333 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -267,13 +267,15 @@ class CUDT // // NOTE: Use notation with X*1000*1000*... instead of // million zeros in a row. - static const int COMM_RESPONSE_MAX_EXP = 16; - static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; - static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; - static const int32_t COMM_SYN_INTERVAL_US = 10*1000; - static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; - static const uint16_t MAX_WEIGHT = 32767; - static const size_t ACK_WND_SIZE = 1024; + static const int COMM_RESPONSE_MAX_EXP = 16; + static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000; + static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000; + static const int32_t COMM_SYN_INTERVAL_US = 10*1000; + static const int COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS = 3000; + static const uint16_t MAX_WEIGHT = 32767; + static const size_t ACK_WND_SIZE = 1024; + static const int INITIAL_RTT = 10 * COMM_SYN_INTERVAL_US; + static const int INITIAL_RTTVAR = INITIAL_RTT / 2; int handshakeVersion() { @@ -731,8 +733,14 @@ class CUDT int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iRTT; // RTT, in microseconds - int m_iRTTVar; // RTT variance + int m_iRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + // of an endpoint's RTT samples), in microseconds + int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair + // at the receiver side or received by the sender from an ACK packet. + // It's used to reset the initial value of smoothed RTT (m_iRTT) + // at the beginning of transmission (including the one taken from + // cache). False by default. int m_iDeliveryRate; // Packet arrival rate at the receiver side int m_iByteDeliveryRate; // Byte arrival rate at the receiver side From 782a27f2328c8e9fbd4481dff56b11ecbea74e9f Mon Sep 17 00:00:00 2001 From: Maria Sharabayko <41019697+mbakholdina@users.noreply.github.com> Date: Thu, 29 Apr 2021 10:00:54 +0200 Subject: [PATCH 052/683] [core] Renamed m_iRTT variable to m_iSRTT (#1971) --- docs/API/statistics.md | 19 +++---------- srtcore/cache.cpp | 28 +++++++++--------- srtcore/cache.h | 18 ++++++------ srtcore/common.h | 2 +- srtcore/congctl.cpp | 18 ++++++------ srtcore/congctl.h | 2 +- srtcore/core.cpp | 64 +++++++++++++++++++++--------------------- srtcore/core.h | 6 ++-- srtcore/crypto.cpp | 2 +- srtcore/group.cpp | 4 +-- 10 files changed, 76 insertions(+), 87 deletions(-) diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 156224fd0..334437bc7 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -539,21 +539,10 @@ at that moment. #### msRTT -Calculated Round trip time (RTT), in milliseconds. Sender and Receiver. \ -The value is calculated by the receiver based on the incoming ACKACK control packets -(used by sender to acknowledge ACKs from receiver). - -The RTT (Round-Trip time) is the sum of two STT (Single-Trip time) -values, one from agent to peer, and one from peer to agent. Note that **the -measurement method is different than in TCP**. SRT measures only the "reverse -RTT", that is, the time measured at the receiver between sending a `UMSG_ACK` -message until receiving the sender's `UMSG_ACKACK` response message (with the -same journal). This happens to be a little different from the "forward RTT" -measured in TCP, which is the time between sending a data packet of a particular -sequence number and receiving `UMSG_ACK` with a sequence number that is later -by 1. Forward RTT isn't being measured or reported in SRT, although some -research works have shown that these values, even though they should be the same, -happen to differ; "reverse RTT" seems to be more optimistic. +Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds. +Available both for sender and receiver. + +See [Section 4.10. Round-Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) and [[RFC6298] Paxson, V., Allman, M., Chu, J., and M. Sargent, "Computing TCP's Retransmission Timer"](https://datatracker.ietf.org/doc/html/rfc6298) for more details. #### mbpsBandwidth diff --git a/srtcore/cache.cpp b/srtcore/cache.cpp index bc9640655..6dfcbba1f 100644 --- a/srtcore/cache.cpp +++ b/srtcore/cache.cpp @@ -50,14 +50,14 @@ using namespace std; CInfoBlock& CInfoBlock::copyFrom(const CInfoBlock& obj) { std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP); - m_iIPversion = obj.m_iIPversion; - m_ullTimeStamp = obj.m_ullTimeStamp; - m_iRTT = obj.m_iRTT; - m_iBandwidth = obj.m_iBandwidth; - m_iLossRate = obj.m_iLossRate; + m_iIPversion = obj.m_iIPversion; + m_ullTimeStamp = obj.m_ullTimeStamp; + m_iSRTT = obj.m_iSRTT; + m_iBandwidth = obj.m_iBandwidth; + m_iLossRate = obj.m_iLossRate; m_iReorderDistance = obj.m_iReorderDistance; - m_dInterval = obj.m_dInterval; - m_dCWnd = obj.m_dCWnd; + m_dInterval = obj.m_dInterval; + m_dCWnd = obj.m_dCWnd; return *this; } @@ -84,14 +84,14 @@ CInfoBlock* CInfoBlock::clone() CInfoBlock* obj = new CInfoBlock; std::copy(m_piIP, m_piIP + 4, obj->m_piIP); - obj->m_iIPversion = m_iIPversion; - obj->m_ullTimeStamp = m_ullTimeStamp; - obj->m_iRTT = m_iRTT; - obj->m_iBandwidth = m_iBandwidth; - obj->m_iLossRate = m_iLossRate; + obj->m_iIPversion = m_iIPversion; + obj->m_ullTimeStamp = m_ullTimeStamp; + obj->m_iSRTT = m_iSRTT; + obj->m_iBandwidth = m_iBandwidth; + obj->m_iLossRate = m_iLossRate; obj->m_iReorderDistance = m_iReorderDistance; - obj->m_dInterval = m_dInterval; - obj->m_dCWnd = m_dCWnd; + obj->m_dInterval = m_dInterval; + obj->m_dCWnd = m_dCWnd; return obj; } diff --git a/srtcore/cache.h b/srtcore/cache.h index 71ec4435a..a39313f61 100644 --- a/srtcore/cache.h +++ b/srtcore/cache.h @@ -235,15 +235,15 @@ template class CCache class CInfoBlock { public: - uint32_t m_piIP[4]; // IP address, machine read only, not human readable format - int m_iIPversion; // Address family: AF_INET or AF_INET6 - uint64_t m_ullTimeStamp; // last update time - int m_iRTT; // RTT - int m_iBandwidth; // estimated bandwidth - int m_iLossRate; // average loss rate - int m_iReorderDistance; // packet reordering distance - double m_dInterval; // inter-packet time, congestion control - double m_dCWnd; // congestion window size, congestion control + uint32_t m_piIP[4]; // IP address, machine read only, not human readable format. + int m_iIPversion; // Address family: AF_INET or AF_INET6. + uint64_t m_ullTimeStamp; // Last update time. + int m_iSRTT; // Smoothed RTT. + int m_iBandwidth; // Estimated link bandwidth. + int m_iLossRate; // Average loss rate. + int m_iReorderDistance; // Packet reordering distance. + double m_dInterval; // Inter-packet time (Congestion Control). + double m_dCWnd; // Congestion window size (Congestion Control). public: CInfoBlock() {} // NOTE: leaves uninitialized diff --git a/srtcore/common.h b/srtcore/common.h index 6c9948351..8a47a0c6c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -284,7 +284,7 @@ enum ETransmissionEvent { TEV_INIT, // --> After creation, and after any parameters were updated. TEV_ACK, // --> When handling UMSG_ACK - older CCC:onAck() - TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::RTT(). + TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::SRTT(). TEV_LOSSREPORT, // --> When handling UMSG_LOSSREPORT - older CCC::onLoss() TEV_CHECKTIMER, // --> See TEV_CHT_REXMIT TEV_SEND, // --> When the packet is scheduled for sending - older CCC::onPktSent diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 721542c92..0b00929b7 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -358,11 +358,11 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize=" << m_dCWndSize << "/" << m_dMaxCWndSize << " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT=" - << m_parent->RTT() << " RCIV=" << m_iRCInterval); + << m_parent->SRTT() << " RCIV=" << m_iRCInterval); } } else @@ -374,9 +374,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16; + m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->SRTT() + m_iRCInterval) + 16; HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize=" - << m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT() + << m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->SRTT() << " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = " << m_parent->deliveryRate() << " pkts/s)"); } @@ -481,9 +481,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT=" - << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")"); + << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } @@ -491,7 +491,7 @@ class FileCC : public SrtCongestionControlBase m_bLoss = true; // TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo()); - const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod; + const int pktsInFlight = m_parent->SRTT() / m_dPktSndPeriod; const int numPktsLost = m_parent->sndLossLength(); const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0; @@ -581,9 +581,9 @@ class FileCC : public SrtCongestionControlBase } else { - m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval); + m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")"); + << setprecision(6) << m_dCWndSize << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 4f887f8be..6419605c0 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -146,7 +146,7 @@ class SrtCongestionControlBase //int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead. //int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo(). //int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead. - //int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead. + //int m_RTT; // NOT REQUIRED. Use m_parent->SRTT() instead. //char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead. // Constructor in protected section so that this class is semi-abstract. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7f23e2653..c4ff3545c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -932,7 +932,7 @@ void CUDT::open() m_pRNode->m_bOnList = false; // Set initial values of smoothed RTT and RTT variance. - m_iRTT = INITIAL_RTT; + m_iSRTT = INITIAL_RTT; m_iRTTVar = INITIAL_RTTVAR; m_bIsFirstRTTReceived = false; @@ -3707,7 +3707,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) log << "startConnect: END. Parameters:" " mss=" << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() - << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); + << " cwnd-size=" << m_CongCtl->cgWindowSize() << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); } // Asynchronous connection @@ -4553,14 +4553,14 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE CInfoBlock::convert(m_PeerAddr, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { - m_iRTT = ib.m_iRTT; - m_iRTTVar = ib.m_iRTT / 2; + m_iSRTT = ib.m_iSRTT; + m_iRTTVar = ib.m_iSRTT / 2; m_iBandwidth = ib.m_iBandwidth; } #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Connect", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); + m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar); #endif SRT_REJECT_REASON rr = setupCC(); @@ -5457,14 +5457,14 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, CInfoBlock::convert(peer, ib.m_piIP); if (m_pCache->lookup(&ib) >= 0) { - m_iRTT = ib.m_iRTT; - m_iRTTVar = ib.m_iRTT / 2; + m_iSRTT = ib.m_iSRTT; + m_iRTTVar = ib.m_iSRTT / 2; m_iBandwidth = ib.m_iBandwidth; } #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Accept", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, m_iRTTVar); + m_bIsFirstRTTReceived, -1, m_iSRTT, m_iRTTVar); #endif m_PeerAddr = peer; @@ -5683,7 +5683,7 @@ SRT_REJECT_REASON CUDT::setupCC() HLOGC(rslog.Debug, log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" - << " rtt=" << m_iRTT << " bw=" << m_iBandwidth); + << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET))) { @@ -5749,7 +5749,7 @@ void CUDT::checkSndTimers(Whether2RegenKm regen) { HLOGC(cnlog.Debug, log << "checkSndTimers: HS SIDE: INITIATOR, considering legacy handshake with timebase"); // Legacy method for HSREQ, only if initiator. - considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iRTT * 3 / 2)); + considerLegacySrtHandshake(m_tsSndHsLastTime + microseconds_from(m_iSRTT * 3 / 2)); } else { @@ -5926,13 +5926,13 @@ bool CUDT::closeInternal() CInfoBlock ib; ib.m_iIPversion = m_PeerAddr.family(); CInfoBlock::convert(m_PeerAddr, ib.m_piIP); - ib.m_iRTT = m_iRTT; + ib.m_iSRTT = m_iSRTT; ib.m_iBandwidth = m_iBandwidth; m_pCache->update(&ib); #if SRT_DEBUG_RTT s_rtt_trace.trace(steady_clock::now(), "Cache", -1, -1, - m_bIsFirstRTTReceived, -1, m_iRTT, -1); + m_bIsFirstRTTReceived, -1, m_iSRTT, -1); #endif m_bConnected = false; @@ -7115,7 +7115,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktFlowWindow = m_iFlowWindowSize; perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); - perf->msRTT = (double)m_iRTT / 1000.0; + perf->msRTT = (double)m_iSRTT / 1000.0; perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; perf->byteMSS = m_config.iMSS; @@ -7529,7 +7529,7 @@ void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } // update next NAK time, which should wait enough time for the retansmission, but not too long - m_tdNAKInterval = microseconds_from(m_iRTT + 4 * m_iRTTVar); + m_tdNAKInterval = microseconds_from(m_iSRTT + 4 * m_iRTTVar); // Fix the NAKreport period according to the congctl m_tdNAKInterval = @@ -7761,7 +7761,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { // If the ACK was just sent already AND elapsed time did not exceed RTT, if ((steady_clock::now() - m_tsLastAckTime) < - (microseconds_from(m_iRTT + 4 * m_iRTTVar))) + (microseconds_from(m_iSRTT + 4 * m_iRTTVar))) { HLOGC(xtlog.Debug, log << "sendCtrl(UMSG_ACK): ACK %" << ack << " just sent - too early to repeat"); return nbsent; @@ -7792,7 +7792,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // number), but still the numbers are from exactly the same domain. m_iAckSeqNo = CAckNo::incack(m_iAckSeqNo); data[ACKD_RCVLASTACK] = m_iRcvLastAck; - data[ACKD_RTT] = m_iRTT; + data[ACKD_RTT] = m_iSRTT; data[ACKD_RTTVAR] = m_iRTTVar; data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock @@ -8110,15 +8110,15 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); + m_iSRTT = avg_iir<8>(m_iSRTT, rtt); } } else // Transmission is unidirectional. { // Simply take the values of smoothed RTT and RTT variance from // the ACK packet. - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rttvar; } } @@ -8130,14 +8130,14 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point // will also trigger the smoothed RTT reset at the sender side. else if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rttvar; m_bIsFirstRTTReceived = true; } #if SRT_DEBUG_RTT s_rtt_trace.trace(currtime, "ACK", rtt, rttvar, m_bIsFirstRTTReceived, - m_stats.recvTotal, m_iRTT, m_iRTTVar); + m_stats.recvTotal, m_iSRTT, m_iRTTVar); #endif /* Version-dependent fields: @@ -8204,14 +8204,14 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival LOGC(inlog.Warn, log << CONID() << "ACKACK out of order, skipping RTT calculation " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } LOGC(inlog.Error, log << CONID() << "IPE: ACK record not found, can't estimate RTT " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iRTT << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } @@ -8230,8 +8230,8 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival // on subsequent RTT samples (during transmission). if (m_bIsFirstRTTReceived) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iRTT)); - m_iRTT = avg_iir<8>(m_iRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); + m_iSRTT = avg_iir<8>(m_iSRTT, rtt); } // Reset the value of smoothed RTT on the first RTT sample after initialization // (at the beginning of transmission). @@ -8240,14 +8240,14 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival // final smoothed RTT value. else { - m_iRTT = rtt; + m_iSRTT = rtt; m_iRTTVar = rtt / 2; m_bIsFirstRTTReceived = true; } #if SRT_DEBUG_RTT s_rtt_trace.trace(tsArrival, "ACKACK", rtt, -1, m_bIsFirstRTTReceived, - -1, m_iRTT, m_iRTTVar); + -1, m_iSRTT, m_iRTTVar); #endif updateCC(TEV_ACKACK, EventVariant(ack)); @@ -8725,7 +8725,7 @@ void CUDT::updateSrtSndSettings() { /* We are TsbPd sender */ // XXX Check what happened here. - // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iRTT + (4 * m_iRTTVar)) / 1000); + // m_iPeerTsbPdDelay_ms = m_CongCtl->getSndPeerTsbPdDelay();// + ((m_iSRTT + (4 * m_iRTTVar)) / 1000); /* * For sender to apply Too-Late Packet Drop * option (m_bTLPktDrop) must be enabled and receiving peer shall support it @@ -8802,7 +8802,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime // protect m_iSndLastDataAck from updating by ACK processing UniqueLock ackguard(m_RecvAckLock); const steady_clock::time_point time_now = steady_clock::now(); - const steady_clock::time_point time_nak = time_now - microseconds_from(m_iRTT - 4 * m_iRTTVar); + const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) { @@ -8841,7 +8841,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) - << " RTT=" << m_iRTT << " RTTVar=" << m_iRTTVar + << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; } @@ -10758,7 +10758,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea else { steady_clock::duration exp_timeout = - microseconds_from(m_iEXPCount * (m_iRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); + microseconds_from(m_iEXPCount * (m_iSRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); if (exp_timeout < (m_iEXPCount * m_tdMinExpInterval)) exp_timeout = m_iEXPCount * m_tdMinExpInterval; next_exp_time = m_tsLastRspTime + exp_timeout; @@ -10834,7 +10834,7 @@ void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) * in the sender's buffer will be added to loss list and retransmitted. */ - const uint64_t rtt_syn = (m_iRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); + const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); if (currtime <= (m_tsLastRspAckTime + microseconds_from(exp_int_us))) diff --git a/srtcore/core.h b/srtcore/core.h index a89c23333..dbb1ab5f9 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -302,7 +302,7 @@ class CUDT void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); bool isOPT_TsbPd() const { return m_config.bTSBPD; } - int RTT() const { return m_iRTT; } + int SRTT() const { return m_iSRTT; } int RTTVar() const { return m_iRTTVar; } int32_t sndSeqNo() const { return m_iSndCurrSeqNo; } int32_t schedSeqNo() const { return m_iSndNextSeqNo; } @@ -733,12 +733,12 @@ class CUDT int m_iEXPCount; // Expiration counter int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + int m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. - // It's used to reset the initial value of smoothed RTT (m_iRTT) + // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. int m_iDeliveryRate; // Packet arrival rate at the receiver side diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 4753e7338..5351e1afe 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -439,7 +439,7 @@ void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED) * then (re-)send handshake request. */ if (((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0)) - && ((m_SndKmLastTime + srt::sync::microseconds_from((m_parent->RTT() * 3)/2)) <= now)) + && ((m_SndKmLastTime + srt::sync::microseconds_from((m_parent->SRTT() * 3)/2)) <= now)) { for (int ki = 0; ki < 2; ki++) { diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 032ac1580..fbd9664b2 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2978,7 +2978,7 @@ class StabilityTracer m_fout << u.id() << ","; m_fout << weight << ","; m_fout << u.peerLatency_us() << ","; - m_fout << u.RTT() << ","; + m_fout << u.SRTT() << ","; m_fout << u.RTTVar() << ","; m_fout << stability_tmo_us << ","; m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; @@ -3039,7 +3039,7 @@ static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::ste const int64_t stability_tout_us = is_activation_phase ? initial_stabtout_us // activation phase - : min(max(min_stability_us, 2 * u.RTT() + 4 * u.RTTVar()), latency_us); + : min(max(min_stability_us, 2 * u.SRTT() + 4 * u.RTTVar()), latency_us); const steady_clock::time_point last_rsp = max(u.freshActivationStart(), u.lastRspTime()); const steady_clock::duration td_response = currtime - last_rsp; From 61dda69be32408192bf6c628a8f3ba5839802aa7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 29 Apr 2021 11:53:32 +0200 Subject: [PATCH 053/683] [apps] Fixed nonblocking client example (#1973) - Fixed setting SRTO_RCVSYN and SRTO_SNDSYN to 0. - Print reject reason. --- examples/example-client-nonblock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/example-client-nonblock.c b/examples/example-client-nonblock.c index b711f5a38..bbaeb0c44 100644 --- a/examples/example-client-nonblock.c +++ b/examples/example-client-nonblock.c @@ -31,7 +31,7 @@ int main(int argc, char** argv) { int ss, st; struct sockaddr_in sa; - int yes = 1; + const int no = 0; const char message [] = "This message should be sent to the other side"; if (argc != 3) { @@ -66,8 +66,8 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) - || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &yes, sizeof yes)) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &no, sizeof no) + || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &no, sizeof no)) { fprintf(stderr, "SRTO_SNDSYN or SRTO_RCVSYN: %s\n", srt_getlasterror_str()); return 1; @@ -101,7 +101,7 @@ int main(int argc, char** argv) SRT_SOCKSTATUS state = srt_getsockstate(ss); if (state != SRTS_CONNECTED || rlen > 0) // rlen > 0 - an error notification { - fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str()); + fprintf(stderr, "srt_epoll_wait: reject reason %s\n", srt_rejectreason_str(srt_getrejectreason(rready))); return 1; } From 9848d68371e22b29f9e32c52ad6cd0e1bb81cc88 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 29 Apr 2021 11:54:12 +0200 Subject: [PATCH 054/683] [core] TSBPD logic extracted from CRcvBuffer. (#1968) Refactoring, no functional changes are expected. New CTsbpdTime class to operate with TSBPD timing. --- srtcore/buffer.cpp | 301 +++-------------------------------------- srtcore/buffer.h | 56 ++------ srtcore/core.cpp | 2 +- srtcore/filelist.maf | 6 +- srtcore/group.cpp | 5 +- srtcore/tsbpd_time.cpp | 164 ++++++++++++++++++++++ srtcore/tsbpd_time.h | 161 ++++++++++++++++++++++ 7 files changed, 360 insertions(+), 335 deletions(-) create mode 100644 srtcore/tsbpd_time.cpp create mode 100644 srtcore/tsbpd_time.h diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 20f5da3d8..660f2b335 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -817,9 +817,6 @@ CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) , m_iAckedPktsCount(0) , m_iAckedBytesCount(0) , m_uAvgPayloadSz(7 * 188) - , m_bTsbPdMode(false) - , m_tdTsbPdDelay(0) - , m_bTsbPdWrapCheck(false) { m_pUnit = new CUnit*[m_iSize]; for (int i = 0; i < m_iSize; ++i) @@ -909,7 +906,8 @@ int CRcvBuffer::readBuffer(char* data, int len) int rs = len; IF_HEAVY_LOGGING(char* begin = data); - const steady_clock::time_point now = (m_bTsbPdMode ? steady_clock::now() : steady_clock::time_point()); + const bool bTsbPdEnabled = m_tsbpd.isEnabled(); + const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); HLOGC(brlog.Debug, log << CONID() << "readBuffer: start=" << p << " lastack=" << lastack); while ((p != lastack) && (rs > 0)) @@ -922,7 +920,7 @@ int CRcvBuffer::readBuffer(char* data, int len) const CPacket& pkt = m_pUnit[p]->m_Packet; - if (m_bTsbPdMode) + if (bTsbPdEnabled) { HLOGC(brlog.Debug, log << CONID() << "readBuffer: chk if time2play:" @@ -1427,7 +1425,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& { w_tsbpdtime = steady_clock::time_point(); - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { const CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) @@ -1649,7 +1647,7 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) { timespan = 0; - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { // Get a valid startpos. // Skip invalid entries in the beginning, if any. @@ -1731,306 +1729,45 @@ void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) m_pUnit[i]->m_iFlag = CUnit::DROPPED; } -steady_clock::time_point CRcvBuffer::getTsbPdTimeBase(uint32_t timestamp_us) -{ - /* - * Packet timestamps wrap around every 01h11m35s (32-bit in usec) - * When added to the peer start time (base time), - * wrapped around timestamps don't provide a valid local packet delevery time. - * - * A wrap check period starts 30 seconds before the wrap point. - * In this period, timestamps smaller than 30 seconds are considered to have wrapped around (then adjusted). - * The wrap check period ends 30 seconds after the wrap point, afterwhich time base has been adjusted. - */ - int64_t carryover = 0; - - // This function should generally return the timebase for the given timestamp_us. - // It's assumed that the timestamp_us, for which this function is being called, - // is received as monotonic clock. This function then traces the changes in the - // timestamps passed as argument and catches the moment when the 64-bit timebase - // should be increased by a "segment length" (MAX_TIMESTAMP+1). - - // The checks will be provided for the following split: - // [INITIAL30][FOLLOWING30]....[LAST30] <-- == CPacket::MAX_TIMESTAMP - // - // The following actions should be taken: - // 1. Check if this is [LAST30]. If so, ENTER TSBPD-wrap-check state - // 2. Then, it should turn into [INITIAL30] at some point. If so, use carryover MAX+1. - // 3. Then it should switch to [FOLLOWING30]. If this is detected, - // - EXIT TSBPD-wrap-check state - // - save the carryover as the current time base. - - if (m_bTsbPdWrapCheck) - { - // Wrap check period. - - if (timestamp_us < TSBPD_WRAP_PERIOD) - { - carryover = int64_t(CPacket::MAX_TIMESTAMP) + 1; - } - // timestamp_us >= TSBPD_WRAP_PERIOD - else if (timestamp_us <= (TSBPD_WRAP_PERIOD * 2)) - { - /* Exiting wrap check period (if for packet delivery head) */ - m_bTsbPdWrapCheck = false; - m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1); - LOGC(tslog.Debug, - log << "tsbpd wrap period ends with ts=" << timestamp_us << " - NEW TIME BASE: " - << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us"); - } - } - // Check if timestamp_us is in the last 30 seconds before reaching the MAX_TIMESTAMP. - else if (timestamp_us > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) - { - /* Approching wrap around point, start wrap check period (if for packet delivery head) */ - m_bTsbPdWrapCheck = true; - LOGC(tslog.Debug, - log << "tsbpd wrap period begins with ts=" << timestamp_us << " drift: " << m_DriftTracer.drift() - << "us."); - } - - return (m_tsTsbPdTimeBase + microseconds_from(carryover)); -} - void CRcvBuffer::applyGroupTime(const steady_clock::time_point& timebase, bool wrp, uint32_t delay, const steady_clock::duration& udrift) { - // Same as setRcvTsbPdMode, but predicted to be used for group members. - // This synchronizes the time from the INTERNAL TIMEBASE of an existing - // socket's internal timebase. This is required because the initial time - // base stays always the same, whereas the internal timebase undergoes - // adjustment as the 32-bit timestamps in the sockets wrap. The socket - // newly added to the group must get EXACTLY the same internal timebase - // or otherwise the TsbPd time calculation will ship different results - // on different sockets. - - m_bTsbPdMode = true; - - m_tsTsbPdTimeBase = timebase; - m_bTsbPdWrapCheck = wrp; - m_tdTsbPdDelay = microseconds_from(delay); - m_DriftTracer.forceDrift(count_microseconds(udrift)); + m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); } void CRcvBuffer::applyGroupDrift(const steady_clock::time_point& timebase, bool wrp, const steady_clock::duration& udrift) { - // This is only when a drift was updated on one of the group members. - HLOGC(brlog.Debug, - log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift) - << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase)); - - m_tsTsbPdTimeBase = timebase; - m_bTsbPdWrapCheck = wrp; - - m_DriftTracer.forceDrift(count_microseconds(udrift)); + m_tsbpd.applyGroupDrift(timebase, wrp, udrift); } -bool CRcvBuffer::getInternalTimeBase(steady_clock::time_point& w_timebase, steady_clock::duration& w_udrift) +void CRcvBuffer::getInternalTimeBase(steady_clock::time_point& w_timebase, bool& w_wrp, steady_clock::duration& w_udrift) { - w_timebase = m_tsTsbPdTimeBase; - w_udrift = microseconds_from(m_DriftTracer.drift()); - return m_bTsbPdWrapCheck; -} - -steady_clock::time_point CRcvBuffer::getPktTsbPdTime(uint32_t timestamp) -{ - const steady_clock::time_point time_base = getTsbPdTimeBase(timestamp); - - // Display only ingredients, not the result, as the result will - // be displayed anyway in the next logs. - HLOGC(brlog.Debug, - log << "getPktTsbPdTime: TIMEBASE=" << FormatTime(time_base) << " + dTS=" << timestamp - << "us + LATENCY=" << FormatDuration(m_tdTsbPdDelay) << " + uDRIFT=" << m_DriftTracer.drift()); - return (time_base + m_tdTsbPdDelay + microseconds_from(timestamp + m_DriftTracer.drift())); + return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift); } -int CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const steady_clock::duration& delay) +steady_clock::time_point CRcvBuffer::getPktTsbPdTime(uint32_t usPktTimestamp) { - m_bTsbPdMode = true; - m_bTsbPdWrapCheck = false; - - // Timebase passed here comes is calculated as: - // >>> CTimer::getTime() - ctrlpkt->m_iTimeStamp - // where ctrlpkt is the packet with SRT_CMD_HSREQ message. - // - // This function is called in the HSREQ reception handler only. - m_tsTsbPdTimeBase = timebase; - // XXX Seems like this may not work correctly. - // At least this solution this way won't work with application-supplied - // timestamps. For that case the timestamps should be taken exclusively - // from the data packets because in case of application-supplied timestamps - // they come from completely different server and undergo different rules - // of network latency and drift. - m_tdTsbPdDelay = delay; - return 0; -} - -#ifdef SRT_DEBUG_TSBPD_DRIFT -void CRcvBuffer::printDriftHistogram(int64_t iDrift) -{ - /* - * Build histogram of drift values - * First line (ms): <=-10.0 -9.0 ... -1.0 - 0.0 + 1.0 ... 9.0 >=10.0 - * Second line (ms): -0.9 ... -0.1 - 0.0 + 0.1 ... 0.9 - * 0 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 1 0 0 0 0 0 0 - * 0 0 0 0 0 0 0 0 0 - 0 + 0 0 0 0 0 0 0 0 0 - */ - iDrift /= 100; // uSec to 100 uSec (0.1ms) - if (-10 < iDrift && iDrift < 10) - { - /* Fill 100us histogram -900 .. 900 us 100 us increments */ - m_TsbPdDriftHisto100us[10 + iDrift]++; - } - else - { - /* Fill 1ms histogram <=-10.0, -9.0 .. 9.0, >=10.0 ms in 1 ms increments */ - iDrift /= 10; // 100uSec to 1ms - if (-10 < iDrift && iDrift < 10) - m_TsbPdDriftHisto1ms[10 + iDrift]++; - else if (iDrift <= -10) - m_TsbPdDriftHisto1ms[0]++; - else - m_TsbPdDriftHisto1ms[20]++; - } - ++m_iTsbPdDriftNbSamples; - if ((m_iTsbPdDriftNbSamples % TSBPD_DRIFT_PRT_SAMPLES) == 0) - { - int* histo = m_TsbPdDriftHisto1ms; - - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", - histo[0], - histo[1], - histo[2], - histo[3], - histo[4], - histo[5], - histo[6], - histo[7], - histo[8], - histo[9], - histo[10]); - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n", - histo[11], - histo[12], - histo[13], - histo[14], - histo[15], - histo[16], - histo[17], - histo[18], - histo[19], - histo[20]); - - histo = m_TsbPdDriftHisto100us; - fprintf(stderr, - " %4d %4d %4d %4d %4d %4d %4d %4d %4d - %4d + ", - histo[1], - histo[2], - histo[3], - histo[4], - histo[5], - histo[6], - histo[7], - histo[8], - histo[9], - histo[10]); - fprintf(stderr, - "%4d %4d %4d %4d %4d %4d %4d %4d %4d\n", - histo[11], - histo[12], - histo[13], - histo[14], - histo[15], - histo[16], - histo[17], - histo[18], - histo[19]); - - m_iTsbPdDriftNbSamples = 0; - } + // Updating TSBPD time here is not very accurate and prevents from making the function constant. + // For now preserving the existing behavior. + m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); + return m_tsbpd.getPktTsbPdTime(usPktTimestamp); } -void CRcvBuffer::printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg) +void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const steady_clock::duration& delay) { - fprintf(stderr, - "%s: tsbpd offset=%d drift=%d usec\n", - FormatTime(steady_clock::now()).c_str(), - tsbPdOffset, - tsbPdDriftAvg); - memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); - memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); + const bool no_wrap_check = false; + m_tsbpd.setTsbPdMode(timebase, no_wrap_check, delay); } -#endif /* SRT_DEBUG_TSBPD_DRIFT */ bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, - Mutex& mutex_to_lock, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { - if (!m_bTsbPdMode) // Not checked unless in TSBPD mode - return false; - /* - * TsbPD time drift correction - * TsbPD time slowly drift over long period depleting decoder buffer or raising latency - * Re-evaluate the time adjustment value using a receiver control packet (ACK-ACK). - * ACK-ACK timestamp is RTT/2 ago (in sender's time base) - * Data packet have origin time stamp which is older when retransmitted so not suitable for this. - * - * Every TSBPD_DRIFT_MAX_SAMPLES packets, the average drift is calculated - * if -TSBPD_DRIFT_MAX_VALUE < avgTsbPdDrift < TSBPD_DRIFT_MAX_VALUE uSec, pass drift value to RcvBuffer to adjust - * delevery time. if outside this range, adjust this->TsbPdTimeOffset and RcvBuffer->TsbPdTimeBase by - * +-TSBPD_DRIFT_MAX_VALUE uSec to maintain TsbPdDrift values in reasonable range (-5ms .. +5ms). - */ - - // Note important thing: this function is being called _EXCLUSIVELY_ in the handler - // of UMSG_ACKACK command reception. This means that the timestamp used here comes - // from the CONTROL domain, not DATA domain (timestamps from DATA domain may be - // either schedule time or a time supplied by the application). - - const steady_clock::duration iDrift = - steady_clock::now() - (getTsbPdTimeBase(timestamp_us) + microseconds_from(timestamp_us)); - - enterCS(mutex_to_lock); - - bool updated = m_DriftTracer.update(count_microseconds(iDrift)); - -#ifdef SRT_DEBUG_TSBPD_DRIFT - printDriftHistogram(count_microseconds(iDrift)); -#endif /* SRT_DEBUG_TSBPD_DRIFT */ - - if (updated) - { -#ifdef SRT_DEBUG_TSBPD_DRIFT - printDriftOffset(m_DriftTracer.overdrift(), m_DriftTracer.drift()); -#endif /* SRT_DEBUG_TSBPD_DRIFT */ - -#if ENABLE_HEAVY_LOGGING - const steady_clock::time_point oldbase = m_tsTsbPdTimeBase; -#endif - steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift()); - m_tsTsbPdTimeBase += overdrift; - - HLOGC(brlog.Debug, - log << "DRIFT=" << FormatDuration(iDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0) - << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift) - << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase)); - } - else - { - HLOGC(brlog.Debug, - log << "DRIFT=" << FormatDuration(iDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); - } - - leaveCS(mutex_to_lock); - w_udrift = iDrift; - w_newtimebase = m_tsTsbPdTimeBase; - return updated; + return m_tsbpd.addDriftSample(timestamp_us, w_udrift, w_newtimebase); } int CRcvBuffer::readMsg(char* data, int len) @@ -2088,7 +1825,7 @@ bool CRcvBuffer::accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playt bool empty = true; - if (m_bTsbPdMode) + if (m_tsbpd.isEnabled()) { w_passack = false; int seq = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index d998fd1b9..6a21df1ce 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -56,8 +56,8 @@ modified by #include "udt.h" #include "list.h" #include "queue.h" +#include "tsbpd_time.h" #include "utilities.h" -#include // The notation used for "circular numbers" in comments: // The "cicrular numbers" are numbers that when increased up to the @@ -392,14 +392,13 @@ class CRcvBuffer /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay /// @param [in] delay aggreed TsbPD delay - /// @return 0 - int setRcvTsbPdMode(const time_point& timebase, const duration& delay); + void setRcvTsbPdMode(const time_point& timebase, const duration& delay); /// Add packet timestamp for drift caclculation and compensation /// @param [in] timestamp packet time stamp - /// @param [ref] lock Mutex that should be locked for the operation + /// @param [out] w_udrift current drift value + /// @param [out] w_newtimebase current TSBPD base time bool addRcvTsbPdDriftSample(uint32_t timestamp, - srt::sync::Mutex& mutex_to_lock, duration& w_udrift, time_point& w_newtimebase); @@ -463,19 +462,12 @@ class CRcvBuffer bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: - /// Get packet delivery local time base (adjusted for wrap around) - /// (Exposed as used publicly in logs) - /// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min - /// @return local delivery time (usec) - time_point getTsbPdTimeBase(uint32_t timestamp_us); - - int64_t getDrift() const { return m_DriftTracer.drift(); } + int64_t getDrift() const { return m_tsbpd.drift(); } public: int32_t getTopMsgno() const; - // @return Wrap check value - bool getInternalTimeBase(time_point& w_tb, duration& w_udrift); + void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift); void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift); void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift); @@ -540,41 +532,9 @@ class CRcvBuffer int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode - duration m_tdTsbPdDelay; // aggreed delay - time_point m_tsTsbPdTimeBase; // localtime base for TsbPd mode - // Note: m_tsTsbPdTimeBase cumulates values from: - // 1. Initial SRT_CMD_HSREQ packet returned value diff to current time: - // == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception - // 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected - // += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8). - // 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively - // from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE - // once the value of average drift exceeds this value in whatever direction. - // += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE - // - // XXX Application-supplied timestamps won't work therefore. This requires separate - // calculation of all these things above. - - bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around - static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) - - /// Max drift (usec) above which TsbPD Time Offset is adjusted - static const int TSBPD_DRIFT_MAX_VALUE = 5000; - /// Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation - static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; - DriftTracer m_DriftTracer; - AvgBufSize m_mavg; -#ifdef SRT_DEBUG_TSBPD_DRIFT - int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment) - int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment) - int m_iTsbPdDriftNbSamples = 0; // Number of samples in sum and histogram - static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram -#endif /* SRT_DEBUG_TSBPD_DRIFT */ + srt::CTsbpdTime m_tsbpd; -#ifdef SRT_DEBUG_TSBPD_OUTJITTER - unsigned long m_ulPdHisto[4][10]; -#endif /* SRT_DEBUG_TSBPD_OUTJITTER */ + AvgBufSize m_mavg; private: CRcvBuffer(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c4ff3545c..c87efeb42 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8262,7 +8262,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), m_RecvLock, + const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 4064c265f..400541499 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -19,12 +19,13 @@ packet.cpp packetfilter.cpp queue.cpp congctl.cpp -srt_c_api.cpp -window.cpp socketconfig.cpp +srt_c_api.cpp srt_compat.c strerror_defs.cpp sync.cpp +tsbpd_time.cpp +window.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp @@ -69,6 +70,7 @@ congctl.h socketconfig.h srt_compat.h threadname.h +tsbpd_time.h utilities.h window.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index fbd9664b2..d6dee49f9 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -48,7 +48,7 @@ bool CUDTGroup::getBufferTimeBase(CUDT* forthesakeof, if (!master) return false; - w_wp = master->m_pRcvBuffer->getInternalTimeBase((w_tb), (w_dr)); + master->m_pRcvBuffer->getInternalTimeBase((w_tb), (w_wp), (w_dr)); // Sanity check if (is_zero(w_tb)) @@ -2734,7 +2734,8 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady steady_clock::time_point this_timebase; steady_clock::duration this_udrift(0); - bool wrp = gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (this_udrift)); + bool wrp = false; + gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); udrift = std::min(udrift, this_udrift); steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp new file mode 100644 index 000000000..0fe07fb7c --- /dev/null +++ b/srtcore/tsbpd_time.cpp @@ -0,0 +1,164 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +#include "tsbpd_time.h" + +#include "logging.h" +#include "logger_defs.h" +#include "packet.h" + +using namespace srt_logging; +using namespace srt::sync; + +namespace srt +{ + +bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, + steady_clock::duration& w_udrift, + steady_clock::time_point& w_newtimebase) +{ + if (!m_bTsbPdMode) + return false; + + const time_point tsNow = steady_clock::now(); + + ScopedLock lck(m_mtxRW); + const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp); + + const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); + + if (updated) + { + IF_HEAVY_LOGGING(const steady_clock::time_point oldbase = m_tsTsbPdTimeBase); + steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift()); + m_tsTsbPdTimeBase += overdrift; + + HLOGC(brlog.Debug, + log << "DRIFT=" << FormatDuration(tdDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0) + << "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift) + << " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase)); + } + else + { + HLOGC(brlog.Debug, + log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); + } + + w_udrift = tdDrift; + w_newtimebase = m_tsTsbPdTimeBase; + + return updated; +} + +void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) +{ + m_bTsbPdMode = true; + m_bTsbPdWrapCheck = wrap; + + // Timebase passed here comes is calculated as: + // Tnow - hspkt.m_iTimeStamp + // where hspkt is the packet with SRT_CMD_HSREQ message. + // + // This function is called in the HSREQ reception handler only. + m_tsTsbPdTimeBase = timebase; + m_tdTsbPdDelay = delay; +} + +void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, + bool wrp, + uint32_t delay, + const steady_clock::duration& udrift) +{ + // Same as setTsbPdMode, but predicted to be used for group members. + // This synchronizes the time from the INTERNAL TIMEBASE of an existing + // socket's internal timebase. This is required because the initial time + // base stays always the same, whereas the internal timebase undergoes + // adjustment as the 32-bit timestamps in the sockets wrap. The socket + // newly added to the group must get EXACTLY the same internal timebase + // or otherwise the TsbPd time calculation will ship different results + // on different member sockets. + + m_bTsbPdMode = true; + + m_tsTsbPdTimeBase = timebase; + m_bTsbPdWrapCheck = wrp; + m_tdTsbPdDelay = microseconds_from(delay); + m_DriftTracer.forceDrift(count_microseconds(udrift)); +} + +void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase, + bool wrp, + const steady_clock::duration& udrift) +{ + // This is only when a drift was updated on one of the group members. + HLOGC(brlog.Debug, + log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift) + << " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase)); + + m_tsTsbPdTimeBase = timebase; + m_bTsbPdWrapCheck = wrp; + + m_DriftTracer.forceDrift(count_microseconds(udrift)); +} + +CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const +{ + const uint64_t carryover_us = + (m_bTsbPdWrapCheck && timestamp_us < TSBPD_WRAP_PERIOD) ? uint64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; + + return (m_tsTsbPdTimeBase + microseconds_from(carryover_us)); +} + +CTsbpdTime::time_point CTsbpdTime::getPktTsbPdTime(uint32_t usPktTimestamp) const +{ + return getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + microseconds_from(m_DriftTracer.drift()); +} + +CTsbpdTime::time_point CTsbpdTime::getPktTsbPdBaseTime(uint32_t usPktTimestamp) const +{ + return getTsbPdTimeBase(usPktTimestamp) + microseconds_from(usPktTimestamp); +} + +void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp) +{ + if (m_bTsbPdWrapCheck) + { + // Wrap check period. + if ((usPktTimestamp >= TSBPD_WRAP_PERIOD) && (usPktTimestamp <= (TSBPD_WRAP_PERIOD * 2))) + { + /* Exiting wrap check period (if for packet delivery head) */ + m_bTsbPdWrapCheck = false; + m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1); + LOGC(tslog.Debug, + log << "tsbpd wrap period ends with ts=" << usPktTimestamp << " - NEW TIME BASE: " + << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us"); + } + return; + } + + // Check if timestamp is in the last 30 seconds before reaching the MAX_TIMESTAMP. + if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) + { + // Approching wrap around point, start wrap check period (if for packet delivery head) + m_bTsbPdWrapCheck = true; + LOGC(tslog.Debug, + log << "tsbpd wrap period begins with ts=" << usPktTimestamp << " drift: " << m_DriftTracer.drift() + << "us."); + } +} + +void CTsbpdTime::getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const +{ + ScopedLock lck(m_mtxRW); + w_tb = m_tsTsbPdTimeBase; + w_udrift = microseconds_from(m_DriftTracer.drift()); + w_wrp = m_bTsbPdWrapCheck; +} + +} // namespace srt diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h new file mode 100644 index 000000000..5dcc7ff3a --- /dev/null +++ b/srtcore/tsbpd_time.h @@ -0,0 +1,161 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_TSBPD_TIME_H +#define INC_SRT_TSBPD_TIME_H + +#include "platform_sys.h" +#include "sync.h" +#include "utilities.h" + +namespace srt +{ + +/// @brief TimeStamp-Based Packet Delivery Mode (TSBPD) time conversion logic. +/// Used by the receiver to calculate delivery time of data packets. +/// See SRT RFC Section "Timestamp-Based Packet Delivery". +class CTsbpdTime +{ + typedef srt::sync::steady_clock steady_clock; + typedef steady_clock::time_point time_point; + typedef steady_clock::duration duration; + typedef srt::sync::Mutex Mutex; + +public: + CTsbpdTime() + : m_bTsbPdMode(false) + , m_tdTsbPdDelay(0) + , m_bTsbPdWrapCheck(false) + { + } + + /// Set TimeStamp-Based Packet Delivery Mode (receiver). + /// @param [in] timebase local time base (uSec) of packet time stamps including buffering delay. + /// @param [in] wrap wrapping period. + /// @param [in] delay negotiated TsbPD delay (buffering latency). + void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + + /// @brief Check if TSBPD logic is enabled. + /// @return true if TSBPD is enabled. + bool isEnabled() const { return m_bTsbPdMode; } + + /// @brief Apply new state derived from other members of a socket group. + /// @param timebase TSBPD base time. + /// @param wrp wrap period (enabled or not). + /// @param delay TSBPD delay. + /// @param udrift clock drift. + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); + + /// @brief Apply new clock state (TSBPD base and drift) derived from other members of a socket group. + /// @param timebase TSBPD base time. + /// @param wrp state of the wrapping period (enabled or disabled). + /// @param udrift clock drift. + void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); + + /// @brief Add new drift sample from an ACK-ACKACK pair. + /// ACKACK packets are sent immediately (except for UDP buffering). + /// + /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. + /// @param [out] w_udrift Current clock drift value. + /// @param [out] w_newtimebase Current TSBPD base time. + /// + /// @return true if TSBPD base time has changed, false otherwise. + bool addDriftSample(uint32_t pktTimestamp, + steady_clock::duration& w_udrift, + steady_clock::time_point& w_newtimebase); + + /// @brief Handle timestamp of data packet when 32-bit integer carryover is about to happen. + /// When packet timestamp approaches CPacket::MAX_TIMESTAMP, the TSBPD base time should be + /// shifted accordingly to correctly handle new packets with timestamps starting from zero. + /// @param usPktTimestamp timestamp field value of a data packet. + void updateTsbPdTimeBase(uint32_t usPktTimestamp); + + /// @brief Get TSBPD base time adjusted for carryover, which occurs when + /// a packet's timestamp exceeds the UINT32_MAX and continues from zero. + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return TSBPD base time for a provided packet timestamp. + time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; + + /// @brief Get packet TSBPD time without buffering delay and clock drift, which is + /// the target time for delivering the packet to an upstream application. + /// Essentially: getTsbPdTimeBase(usPktTimestamp) + usPktTimestamp + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return Packet TSBPD base time without buffering delay. + time_point getPktTsbPdBaseTime(uint32_t usPktTimestamp) const; + + /// @brief Get packet TSBPD time with buffering delay and clock drift, which is + /// the target time for delivering the packet to an upstream application + /// (including drift and carryover effects, if any). + /// Essentially: getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + drift() + /// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds). + /// + /// @return Packet TSBPD time with buffering delay. + time_point getPktTsbPdTime(uint32_t usPktTimestamp) const; + + /// @brief Get current drift value. + /// @return current drift value. + int64_t drift() const { return m_DriftTracer.drift(); } + + /// @brief Get current overdrift value. + /// @return current overdrift value. + int64_t overdrift() const { return m_DriftTracer.overdrift(); } + + /// @brief Get internal state to apply to another member of a socket group. + /// @param w_tb TsbPd base time. + /// @param w_udrift drift value. + /// @param w_wrp wrap check. + void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const; + +private: + bool m_bTsbPdMode; //< Rreceiver buffering and TSBPD is active when true. + duration m_tdTsbPdDelay; //< Negotiated buffering delay. + + /// @brief Local time base for TsbPd. + /// @note m_tsTsbPdTimeBase is changed in the following cases: + /// 1. Initialized upon SRT_CMD_HSREQ packet as the difference with the current time: + /// = (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception. + /// 2. Shifted forward on timestamp overflow (@see CTsbpdTime::updateTsbPdTimeBase), when overflow + /// of the timestamp field value of a data packet is detected. + /// += CPacket::MAX_TIMESTAMP + 1 + /// 3. Clock drift (@see CTsbpdTime::addDriftSample, executed exclusively + /// from ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE + /// once the value of average drift exceeds this value in either direction. + /// += (+/-)TSBPD_DRIFT_MAX_VALUE + /// + /// @note The TSBPD base time is expected to hold the following condition: + /// (PACKET_TIMESTAMP + m_tsTsbPdTimeBase + drift) == NOW. + /// Then it can be used to estimate the origin time of a data packet, and calculate its delivery time + /// with buffering delay applied. + time_point m_tsTsbPdTimeBase; + + /// @note Packet timestamps wrap around every 01h11m35s (32-bit in usec). + /// A wrap check period starts 30 seconds (TSBPD_WRAP_PERIOD) before the wrap point. + /// During the wrap check period, packet timestamps smaller than 30 seconds + /// are considered to have been wrapped around. + /// The wrap check period ends 30 seconds after the wrap point, + /// after which the TSBPD base time is adjusted. + bool m_bTsbPdWrapCheck; // true: check packet time stamp wraparound (overflow). + static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) for timestamp wrapping period. + + /// Maximum clock drift (microseconds) above which TsbPD base time is already adjusted. + static const int TSBPD_DRIFT_MAX_VALUE = 5000; + /// Number of samples (ACKACK packets) on which to perform drift calculation and compensation. + static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; + DriftTracer m_DriftTracer; + + /// Protect simultaneous change of state (read/write). + mutable Mutex m_mtxRW; +}; + +} // namespace srt + +#endif // INC_SRT_TSBPD_TIME_H From 631d0fcb9ba7209e381a8f1927419545ef3d5c41 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 30 Apr 2021 14:56:31 +0200 Subject: [PATCH 055/683] [apps] Removed trailing comma in JSON stats (#1976) --- apps/apputil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index f772beadf..0333c768f 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -552,7 +552,7 @@ class SrtStatsJson : public SrtStatsWriter } // Close the general category entity - output << "}," << pretty_cr << endl; + output << "}" << pretty_cr << endl; return output.str(); } From d43b8366a1e5989a1f3dd784bca5d696cc3bbd77 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 3 May 2021 18:43:33 +0200 Subject: [PATCH 056/683] [docs] Fixed SRTO_RCVLATENCY description of the default value in live mode --- docs/API/API-socket-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 7f0316a6e..3c9f91556 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1172,7 +1172,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). The latency value in the receiving direction of the socket. This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. -**Default value**: 120 ms (depicted as 0) in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). +**Default value**: 120 ms in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). The latency value defines the **minimum** receiver buffering delay before delivering an SRT data packet from a receiving SRT socket to a receiving application. The provided value is used in the connection establishment (handshake exchange) stage From e33f0d3da62a39043cae41081bc2dcc69116e2d4 Mon Sep 17 00:00:00 2001 From: Alex Pokotilo Date: Tue, 4 May 2021 17:56:46 +1000 Subject: [PATCH 057/683] [docs] Added missing with-srt-name build opt description (#1982) --- docs/build/build-options.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 805214338..938d925d2 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -380,6 +380,10 @@ This should be the exact command used as a C compiler, possibly with version suffix, e.g. `clang-1.7.0`. If this option is used together with `--with-compiler-prefix`, its prefix will be added in front. +**`--with-srt-name=`** + +Overrides srt library name adding custom `` + **`--with-extralibs=`** This is an option required for unusual situations when a platform-specific From d9eefd8ae196c69b8df8b47b32ea361126dd9120 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 4 May 2021 12:57:03 +0200 Subject: [PATCH 058/683] [core] Fixed cookie contest (#1517) No longer relying on integer overflow. --- docs/features/handshake.md | 21 +++++++++++++++- srtcore/core.cpp | 50 ++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/docs/features/handshake.md b/docs/features/handshake.md index ef3fcfdbe..05af2cd28 100644 --- a/docs/features/handshake.md +++ b/docs/features/handshake.md @@ -697,7 +697,26 @@ connection will not be made until new, unique cookies are generated (after a delay of up to one minute). In the case of an application "connecting to itself", the cookies will always be identical, and so the connection will never be made. -When one party's cookie value is greater than its peer's, it wins the cookie +```c++ +// Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer. +// m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request. +const int64_t contest = int64_t(m_ConnReq.m_iCookie) - int64_t(m_ConnRes.m_iCookie); + +if ((contest & 0xFFFFFFFF) == 0) +{ + return HSD_DRAW; +} + +if (contest & 0x80000000) +{ + return HSD_RESPONDER; +} + +return HSD_INITIATOR; +``` + +When one party's cookie value is greater than its peer's (based on 32-bit subtraction of both +with potential overflow), it wins the cookie contest and becomes Initiator (the other party becomes the Responder). At this point there are two "handshake flows" possible (at least theoretically): diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c87efeb42..7fe156797 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3827,8 +3827,10 @@ void CUDT::cookieContest() if (m_SrtHsSide != HSD_DRAW) return; - HLOGC(cnlog.Debug, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + LOGC(cnlog.Error, log << "cookieContest: agent=" << m_ConnReq.m_iCookie << " peer=" << m_ConnRes.m_iCookie); + // Here m_ConnReq.m_iCookie is a local cookie value sent in connection request to the peer. + // m_ConnRes.m_iCookie is a cookie value sent by the peer in its connection request. if (m_ConnReq.m_iCookie == 0 || m_ConnRes.m_iCookie == 0) { // Note that it's virtually impossible that Agent's cookie is not ready, this @@ -3841,31 +3843,43 @@ void CUDT::cookieContest() // // The cookie contest must be repeated every time because it // may change the state at some point. - int better_cookie = m_ConnReq.m_iCookie - m_ConnRes.m_iCookie; - - if (better_cookie > 0) - { - m_SrtHsSide = HSD_INITIATOR; + // + // In SRT v1.4.3 and prior the below subtraction was performed in 32-bit arithmetic. + // The result of subtraction can overflow 32-bits. + // Example + // m_ConnReq.m_iCookie = -1480577720; + // m_ConnRes.m_iCookie = 811599203; + // int64_t llBetterCookie = -1480577720 - 811599203 = -2292176923 (FFFF FFFF 7760 27E5); + // int32_t iBetterCookie = 2002790373 (7760 27E5); + // + // Now 64-bit arithmetic is used to calculate the actual result of subtraction. + // The 31-st bit is then checked to check if the resulting is negative in 32-bit aritmetics. + // This way the old contest behavior is preserved, and potential compiler optimisations are avoided. + const int64_t contest = int64_t(m_ConnReq.m_iCookie) - int64_t(m_ConnRes.m_iCookie); + + if ((contest & 0xFFFFFFFF) == 0) + { + // DRAW! The only way to continue would be to force the + // cookies to be regenerated and to start over. But it's + // not worth a shot - this is an extremely rare case. + // This can simply do reject so that it can be started again. + + // Pretend then that the cookie contest wasn't done so that + // it's done again. Cookies are baked every time anew, however + // the successful initial contest remains valid no matter how + // cookies will change. + + m_SrtHsSide = HSD_DRAW; return; } - if (better_cookie < 0) + if (contest & 0x80000000) { m_SrtHsSide = HSD_RESPONDER; return; } - // DRAW! The only way to continue would be to force the - // cookies to be regenerated and to start over. But it's - // not worth a shot - this is an extremely rare case. - // This can simply do reject so that it can be started again. - - // Pretend then that the cookie contest wasn't done so that - // it's done again. Cookies are baked every time anew, however - // the successful initial contest remains valid no matter how - // cookies will change. - - m_SrtHsSide = HSD_DRAW; + m_SrtHsSide = HSD_INITIATOR; } // This function should complete the data for KMX needed for an out-of-band From 97445fc9e553a47379f12dcb3c71b6b98ac91a2b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 5 May 2021 14:51:23 +0200 Subject: [PATCH 059/683] [core] Minor strerror_array_sizes refactoring --- srtcore/api.cpp | 4 +- srtcore/api.h | 4 +- srtcore/core.cpp | 10 ++--- srtcore/core.h | 2 +- srtcore/group_common.cpp | 80 +++++++++++++++++++-------------------- srtcore/group_common.h | 69 +++++++++++++++++---------------- srtcore/strerror_defs.cpp | 27 ++++++------- 7 files changed, 95 insertions(+), 101 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 71e4a8772..5bd307125 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -165,14 +165,14 @@ bool CUDTSocket::readReady() return broken(); } -bool CUDTSocket::writeReady() +bool CUDTSocket::writeReady() const { return (m_pUDT->m_bConnected && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) || broken(); } -bool CUDTSocket::broken() +bool CUDTSocket::broken() const { return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; } diff --git a/srtcore/api.h b/srtcore/api.h index 604f14a99..4ca299b24 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -182,8 +182,8 @@ class CUDTSocket // Instrumentally used by select() and also required for non-blocking // mode check in groups bool readReady(); - bool writeReady(); - bool broken(); + bool writeReady() const; + bool broken() const; private: CUDTSocket(const CUDTSocket&); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7fe156797..35db04142 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3115,7 +3115,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN return false; } - srt::groups::SocketData* f = m_parent->m_GroupMemberData; + groups::SocketData* f = m_parent->m_GroupMemberData; f->weight = link_weight; f->agent = m_parent->m_SelfAddr; @@ -3214,7 +3214,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l // Copy of addSocketToGroup. No idea how many parts could be common, not much. // Check if the socket already is in the group - srt::groups::SocketData* f; + groups::SocketData* f; if (gp->contains(m_SocketID, (f))) { // XXX This is internal error. Report it, but continue @@ -4664,7 +4664,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE HLOGC(cnlog.Debug, log << "group: Socket @" << m_parent->m_SocketID << " fresh connected, setting IDLE"); - srt::groups::SocketData* gi = m_parent->m_GroupMemberData; + groups::SocketData* gi = m_parent->m_GroupMemberData; gi->sndstate = SRT_GST_IDLE; gi->rcvstate = SRT_GST_IDLE; gi->laststatus = SRTS_CONNECTED; @@ -9497,7 +9497,7 @@ int CUDT::processData(CUnit* in_unit) if (m_parent->m_GroupOf) { ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); - srt::groups::SocketData* gi = m_parent->m_GroupMemberData; + groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long @@ -9996,7 +9996,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) if (self->m_parent->m_GroupOf) { - srt::groups::SocketData* gi = self->m_parent->m_GroupMemberData; + groups::SocketData* gi = self->m_parent->m_GroupMemberData; if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); diff --git a/srtcore/core.h b/srtcore/core.h index dbb1ab5f9..2f97d9cfc 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1046,7 +1046,7 @@ class CUDT int64_t m_sndDurationTotal; // total real time for sending - time_point tsLastSampleTime; // last performance sample time + time_point tsLastSampleTime; // last performance sample time int64_t traceSent; // number of packets sent in the last trace interval int64_t traceSentUniq; // number of original packets sent in the last trace interval int64_t traceRecv; // number of packets received in the last trace interval diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp index 513e5065c..8716e343a 100644 --- a/srtcore/group_common.cpp +++ b/srtcore/group_common.cpp @@ -8,10 +8,10 @@ * */ - /***************************************************************************** - Written by - Haivision Systems Inc. - *****************************************************************************/ +/***************************************************************************** +Written by + Haivision Systems Inc. +*****************************************************************************/ #include "platform_sys.h" @@ -20,43 +20,43 @@ namespace srt { - namespace groups - { +namespace groups +{ - SocketData prepareSocketData(CUDTSocket* s) - { - // This uses default SRT_GST_BROKEN because when the group operation is done, - // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is - // recognized as an initial state of the fresh added socket to the group, - // so some "initial configuration" must be done on it, after which it's - // turned into SRT_GST_RUNNING, that is, it's treated as all others. When - // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned - // up, however, unless the status is simultaneously SRTS_BROKEN. +SocketData prepareSocketData(CUDTSocket* s) +{ + // This uses default SRT_GST_BROKEN because when the group operation is done, + // then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is + // recognized as an initial state of the fresh added socket to the group, + // so some "initial configuration" must be done on it, after which it's + // turned into SRT_GST_RUNNING, that is, it's treated as all others. When + // set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned + // up, however, unless the status is simultaneously SRTS_BROKEN. - // The order of operations is then: - // - add the socket to the group in this "broken" initial state - // - connect the socket (or get it extracted from accept) - // - update the socket state (should be SRTS_CONNECTED) - // - once the connection is established (may take time with connect), set SRT_GST_IDLE - // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING - SocketData sd = { - s->m_SocketID, - s, - -1, - SRTS_INIT, - SRT_GST_BROKEN, - SRT_GST_BROKEN, - -1, - -1, - sockaddr_any(), - sockaddr_any(), - false, - false, - false, - 0 // weight - }; - return sd; - } + // The order of operations is then: + // - add the socket to the group in this "broken" initial state + // - connect the socket (or get it extracted from accept) + // - update the socket state (should be SRTS_CONNECTED) + // - once the connection is established (may take time with connect), set SRT_GST_IDLE + // - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING + SocketData sd = { + s->m_SocketID, + s, + -1, + SRTS_INIT, + SRT_GST_BROKEN, + SRT_GST_BROKEN, + -1, + -1, + sockaddr_any(), + sockaddr_any(), + false, + false, + false, + 0 // weight + }; + return sd; +} - } // namespace groups +} // namespace groups } // namespace srt diff --git a/srtcore/group_common.h b/srtcore/group_common.h index 13ef4ad4a..f6ac123c6 100644 --- a/srtcore/group_common.h +++ b/srtcore/group_common.h @@ -8,10 +8,10 @@ * */ - /***************************************************************************** - Written by - Haivision Systems Inc. - *****************************************************************************/ +/***************************************************************************** +Written by + Haivision Systems Inc. +*****************************************************************************/ #ifndef INC_SRT_GROUP_COMMON_H #define INC_SRT_GROUP_COMMON_H @@ -24,37 +24,36 @@ namespace srt { - namespace groups - { - typedef SRT_MEMBERSTATUS GroupState; - - struct SocketData - { - SRTSOCKET id; // same as ps->m_SocketID - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; - }; - - SocketData prepareSocketData(CUDTSocket* s); - - typedef std::list group_t; - typedef group_t::iterator gli_t; - - } // namespace groups -} // namespace srt +namespace groups +{ +typedef SRT_MEMBERSTATUS GroupState; +struct SocketData +{ + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; +}; + +SocketData prepareSocketData(CUDTSocket* s); + +typedef std::list group_t; +typedef group_t::iterator gli_t; + +} // namespace groups +} // namespace srt #endif // INC_SRT_GROUP_COMMON_H diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index ee32527bf..c37ee276d 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -119,27 +119,22 @@ const char** strerror_array_major [] = { strerror_msgs_again, // MJ_AGAIN = 6 strerror_msgs_peererror, // MJ_PEERERROR = 7 NULL - }; - - -size_t strerror_array_sizes [] = { - 1, - 6, - 3, - 4, - 5, - 15, - 5, - 1, +#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0]) + +const size_t strerror_array_sizes[] = { + SRT_ARRAY_SIZE(strerror_msgs_success) - 1, + SRT_ARRAY_SIZE(strerror_msgs_setup) - 1, + SRT_ARRAY_SIZE(strerror_msgs_connection) - 1, + SRT_ARRAY_SIZE(strerror_msgs_systemres) - 1, + SRT_ARRAY_SIZE(strerror_msgs_filesystem) - 1, + SRT_ARRAY_SIZE(strerror_msgs_notsup) - 1, + SRT_ARRAY_SIZE(strerror_msgs_again) - 1, + SRT_ARRAY_SIZE(strerror_msgs_peererror) - 1, 0 - }; - - - const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; From 41348e8dd0ab19e0a14264669f8b10145642e532 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 6 May 2021 00:21:57 +0800 Subject: [PATCH 060/683] [apps] Replace functions in UDT namespace by functions in srt namespace --- apps/srt-file-transmit.cpp | 8 ++++---- apps/srt-live-transmit.cpp | 2 +- apps/srt-tunnel.cpp | 8 ++++---- apps/transmitmedia.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 9c1ec82f4..d3ea92092 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -321,7 +321,7 @@ bool DoUpload(UriParser& ut, string path, string filename, << tar->GetSRTSocket() << endl; goto exit; } - UDT::setstreamid(tar->GetSRTSocket(), filename); + srt::setstreamid(tar->GetSRTSocket(), filename); } s = tar->GetSRTSocket(); @@ -539,7 +539,7 @@ bool DoDownload(UriParser& us, string directory, string filename, cerr << "Failed to add SRT client to poll" << endl; goto exit; } - id = UDT::getstreamid(s); + id = srt::getstreamid(s); cerr << "Source connected (listener), id [" << id << "]" << endl; connected = true; @@ -550,7 +550,7 @@ bool DoDownload(UriParser& us, string directory, string filename, { if (!connected) { - id = UDT::getstreamid(s); + id = srt::getstreamid(s); cerr << "Source connected (caller), id [" << id << "]" << endl; connected = true; @@ -714,7 +714,7 @@ int main(int argc, char** argv) } else { - UDT::setlogstream(logfile_stream); + srt::setlogstream(logfile_stream); } } diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index bfe4ff590..9aad3209c 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -434,7 +434,7 @@ int main(int argc, char** argv) } else { - UDT::setlogstream(logfile_stream); + srt::setlogstream(logfile_stream); } } diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index e5175ce8b..2c4aacfb3 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -1115,21 +1115,21 @@ int main( int argc, char** argv ) string loglevel = Option(params, "error", o_loglevel); string logfa = Option(params, "", o_logfa); srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel); - UDT::setloglevel(lev); + srt::setloglevel(lev); if (logfa == "") { - UDT::addlogfa(SRT_LOGFA_APP); + srt::addlogfa(SRT_LOGFA_APP); } else { // Add only selected FAs set unknown_fas; set fas = SrtParseLogFA(logfa, &unknown_fas); - UDT::resetlogfa(fas); + srt::resetlogfa(fas); // The general parser doesn't recognize the "app" FA, we check it here. if (unknown_fas.count("app")) - UDT::addlogfa(SRT_LOGFA_APP); + srt::addlogfa(SRT_LOGFA_APP); } string verbo = Option(params, "no", o_verbose); diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index fb4b13748..e5f76206d 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -624,7 +624,7 @@ void SrtModel::Establish(std::string& w_name) if (w_name != "") { Verb() << "Connect with requesting stream [" << w_name << "]"; - UDT::setstreamid(m_sock, w_name); + srt::setstreamid(m_sock, w_name); } else { @@ -667,7 +667,7 @@ void SrtModel::Establish(std::string& w_name) Verb() << "Accepting a client..."; AcceptNewClient(); // This rewrites m_sock with a new SRT socket ("accepted" socket) - w_name = UDT::getstreamid(m_sock); + w_name = srt::getstreamid(m_sock); Verb() << "... GOT CLIENT for stream [" << w_name << "]"; } } From 514f61e9e0d5dbc34dcd5da8f9353c9b7c459a35 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 7 May 2021 16:14:54 +0800 Subject: [PATCH 061/683] [core] Fix comments There is a typo in the comment, and specify the configuration value in comment has the risk of out of sync with source code. --- srtcore/socketconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 6b8a9325e..1db4bb63a 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -993,7 +993,7 @@ struct CSrtConfigSetter // File transfer mode: // - tsbpd: off // - latency: 0 - // - linger: 2 minutes (180s) + // - linger: on // - congctl: file (original UDT congestion control) // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) co.bTSBPD = false; From 88309439b5b71784f14730ecb5c651bbd968a68d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 11:24:38 +0200 Subject: [PATCH 062/683] [core] Improved 'no room to store' log message (#1909) --- srtcore/buffer.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++++ srtcore/buffer.h | 16 +++++++++++ srtcore/core.cpp | 11 ++++---- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 660f2b335..e2efcefff 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1722,6 +1722,75 @@ unsigned CRcvBuffer::getRcvAvgPayloadSize() const return m_uAvgPayloadSz; } +CRcvBuffer::ReadingState CRcvBuffer::debugGetReadingState() const +{ + ReadingState readstate; + + readstate.iNumAcknowledged = 0; + readstate.iNumUnacknowledged = m_iMaxPos; + + if ((NULL != m_pUnit[m_iStartPos]) && (m_pUnit[m_iStartPos]->m_iFlag == CUnit::GOOD)) + { + if (m_tsbpd.isEnabled()) + readstate.tsStart = m_tsbpd.getPktTsbPdTime(m_pUnit[m_iStartPos]->m_Packet.getMsgTimeStamp()); + + readstate.iNumAcknowledged = m_iLastAckPos > m_iStartPos + ? m_iLastAckPos - m_iStartPos + : m_iLastAckPos + (m_iSize - m_iStartPos); + } + + // All further stats are valid if TSBPD is enabled. + if (!m_tsbpd.isEnabled()) + return readstate; + + // m_iLastAckPos points to the first unacknowledged packet + const int iLastAckPos = (m_iLastAckPos - 1) % m_iSize; + if (m_iLastAckPos != m_iStartPos && (NULL != m_pUnit[iLastAckPos]) && (m_pUnit[iLastAckPos]->m_iFlag == CUnit::GOOD)) + { + readstate.tsLastAck = m_tsbpd.getPktTsbPdTime(m_pUnit[iLastAckPos]->m_Packet.getMsgTimeStamp()); + } + + const int iEndPos = (m_iLastAckPos + m_iMaxPos - 1) % m_iSize; + if (m_iMaxPos == 0) + { + readstate.tsEnd = readstate.tsLastAck; + } + else if ((NULL != m_pUnit[iEndPos]) && (m_pUnit[iEndPos]->m_iFlag == CUnit::GOOD)) + { + readstate.tsEnd = m_tsbpd.getPktTsbPdTime(m_pUnit[iEndPos]->m_Packet.getMsgTimeStamp()); + } + + return readstate; +} + +string CRcvBuffer::strFullnessState(const time_point& tsNow) const +{ + const ReadingState bufstate = debugGetReadingState(); + stringstream ss; + + ss << "Space avail " << getAvailBufSize() << "/" << m_iSize; + ss << " pkts. Packets ACKed: " << bufstate.iNumAcknowledged; + if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsLastAck)) + { + ss << " (TSBPD ready in "; + ss << count_milliseconds(bufstate.tsStart - tsNow); + ss << " : "; + ss << count_milliseconds(bufstate.tsLastAck - tsNow); + ss << " ms)"; + } + + ss << ", not ACKed: " << bufstate.iNumUnacknowledged; + if (!is_zero(bufstate.tsStart) && !is_zero(bufstate.tsEnd)) + { + ss << ", timespan "; + ss << count_milliseconds(bufstate.tsEnd - bufstate.tsStart); + ss << " ms"; + } + + ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + return ss.str(); +} + void CRcvBuffer::dropMsg(int32_t msgno, bool using_rexmit_flag) { for (int i = m_iStartPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 6a21df1ce..d32e1d29e 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -348,6 +348,21 @@ class CRcvBuffer /// @return size (bytes) of payload size unsigned getRcvAvgPayloadSize() const; + struct ReadingState + { + time_point tsStart; + time_point tsLastAck; + time_point tsEnd; + int iNumAcknowledged; + int iNumUnacknowledged; + }; + + ReadingState debugGetReadingState() const; + + /// Form a string of the current buffer fullness state. + /// number of packets acknowledged, TSBPD readiness, etc. + std::string strFullnessState(const time_point& tsNow) const; + /// Mark the message to be dropped from the message list. /// @param [in] msgno message number. /// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the @@ -462,6 +477,7 @@ class CRcvBuffer bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); public: + /// @brief Get clock drift in microseconds. int64_t getDrift() const { return m_tsbpd.drift(); } public: diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 35db04142..81eaa9628 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9622,12 +9622,11 @@ int CUDT::processData(CUnit* in_unit) } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet: offset=" - << offset << " avail=" << avail_bufsize - << " ack.seq=" << m_iRcvLastSkipAck << " pkt.seq=" << rpkt.m_iSeqNo - << " rcv-remain=" << m_pRcvBuffer->debugGetSize() - << " drift=" << m_pRcvBuffer->getDrift() - ); + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(steady_clock::now()) + ); + return -1; } } From 2559797ed3284f53704ffbe57c90f47328a28669 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 15:06:35 +0200 Subject: [PATCH 063/683] [core] Main/backup: improved switching rules (#1857) and refactored the related code --- srtcore/core.cpp | 14 +- srtcore/core.h | 11 +- srtcore/filelist.maf | 2 + srtcore/group.cpp | 1322 ++++++++++++++++++-------------------- srtcore/group.h | 144 +++-- srtcore/group_backup.cpp | 159 +++++ srtcore/group_backup.h | 123 ++++ srtcore/group_common.cpp | 3 +- srtcore/group_common.h | 55 +- 9 files changed, 1036 insertions(+), 797 deletions(-) create mode 100644 srtcore/group_backup.cpp create mode 100644 srtcore/group_backup.h diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 81eaa9628..efc866164 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -248,6 +248,7 @@ void CUDT::construct() m_bClosing = false; m_bShutdown = false; m_bBroken = false; + m_bBreakAsUnstable = false; // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; @@ -950,12 +951,13 @@ void CUDT::open() m_tsLastRspAckTime = currtime; m_tsLastSndTime = currtime; - m_iReXmitCount = 1; - m_tsUnstableSince = steady_clock::time_point(); + m_tsUnstableSince = steady_clock::time_point(); m_tsFreshActivation = steady_clock::time_point(); + m_tsWarySince = steady_clock::time_point(); + + m_iReXmitCount = 1; m_iPktCount = 0; m_iLightACKCount = 1; - m_tsNextSendTime = steady_clock::time_point(); m_tdSendTimeDiff = microseconds_from(0); @@ -10777,15 +10779,15 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea next_exp_time = m_tsLastRspTime + exp_timeout; } - if (currtime <= next_exp_time) + if (currtime <= next_exp_time && !m_bBreakAsUnstable) return false; // ms -> us const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds - if ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && - (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US))) + if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && + (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US)))) { // // Connection is broken. diff --git a/srtcore/core.h b/srtcore/core.h index 2f97d9cfc..97eda7436 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -420,6 +420,9 @@ class CUDT SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); + /// @brief Request a socket to be broken due to too long instability (normally by a group). + void breakAsUnstable() { m_bBreakAsUnstable = true; } + void ConnectSignal(ETransmissionEvent tev, EventSlot sl); void DisconnectSignal(ETransmissionEvent tev); @@ -726,6 +729,7 @@ class CUDT volatile bool m_bClosing; // If the UDT entity is closing volatile bool m_bShutdown; // If the peer side has shutdown the connection volatile bool m_bBroken; // If the connection has been broken + volatile bool m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. volatile bool m_bPeerHealth; // If the peer status is normal volatile int m_RejectReason; bool m_bOpened; // If the UDT entity has been opened @@ -908,7 +912,7 @@ class CUDT srt::sync::Mutex m_SendLock; // used to synchronize "send" call srt::sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) - srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics + mutable srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics void initSynch(); void destroySynch(); @@ -1090,8 +1094,9 @@ class CUDT static const int PACKETPAIR_MASK = 0xF; private: // Timers functions - time_point m_tsFreshActivation; // time of fresh activation of the link, or 0 if past the activation phase or idle - time_point m_tsUnstableSince; // time since unexpected ACK delay experienced, or 0 if link seems healthy + time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle + time_point m_tsUnstableSince; // GROUPS: time since unexpected ACK delay experienced, or 0 if link seems healthy + time_point m_tsWarySince; // GROUPS: time since an unstable link has first some response static const int BECAUSE_NO_REASON = 0, // NO BITS BECAUSE_ACK = 1 << 0, diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 400541499..54e15df62 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -29,6 +29,7 @@ window.cpp SOURCES - ENABLE_EXPERIMENTAL_BONDING group.cpp +group_backup.cpp group_common.cpp SOURCES - !ENABLE_STDCXX_SYNC @@ -76,4 +77,5 @@ window.h PRIVATE HEADERS - ENABLE_EXPERIMENTAL_BONDING group.h +group_backup.h group_common.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d6dee49f9..b16bfc349 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -7,6 +7,7 @@ using namespace std; using namespace srt::sync; +using namespace srt::groups; using namespace srt_logging; // The SRT_DEF_VERSION is defined in core.cpp. @@ -636,7 +637,7 @@ inline int Value::fill(void* optval, int len, std::string value) if (size_t(len) < value.size()) return 0; memcpy(optval, value.c_str(), value.size()); - return value.size(); + return (int) value.size(); } template @@ -1716,7 +1717,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // is performed, and this one will result in none-write-ready, this will // be fixed just after returning from this function. - ready_again = ready_again | d->ps->writeReady(); + ready_again = ready_again || d->ps->writeReady(); } w_mc.grpdata_size = i; @@ -2811,23 +2812,42 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) } } -// For sorting group members by priority - -struct FPriorityOrder +/// @brief Compares group members by their weight (higher weight comes first). +struct FCompareByWeight { - // returned true = "elements are in the right order" - static bool check(uint16_t preceding, uint16_t succeeding) + typedef CUDTGroup::gli_t gli_t; + + /// @returns true if the first argument is less than (i.e. is ordered before) the second. + bool operator()(const gli_t preceding, const gli_t succeeding) { - return preceding > succeeding; + return preceding->weight > succeeding->weight; } +}; - typedef CUDTGroup::gli_t gli_t; +// [[using maybe_locked(this->m_GroupLock)]] +BackupMemberState CUDTGroup::sendBackup_QualifyIfStandBy(const gli_t d) +{ + if (!d->ps) + return BKUPST_BROKEN; - bool operator()(gli_t preceding, gli_t succeeding) + const SRT_SOCKSTATUS st = d->ps->getStatus(); + // If the socket is already broken, move it to broken. + if (int(st) >= int(SRTS_BROKEN)) { - return check(preceding->weight, succeeding->weight); + HLOGC(gslog.Debug, + log << "CUDTGroup::send.$" << id() << ": @" << d->id << " became " << SockStatusStr(st) + << ", WILL BE CLOSED."); + return BKUPST_BROKEN; } -}; + + if (st != SRTS_CONNECTED) + { + HLOGC(gslog.Debug, log << "CUDTGroup::send. @" << d->id << " is still " << SockStatusStr(st) << ", skipping."); + return BKUPST_PENDING; + } + + return BKUPST_STANDBY; +} // [[using maybe_locked(this->m_GroupLock)]] bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vector& w_pendingSockets) @@ -2855,12 +2875,75 @@ bool CUDTGroup::send_CheckIdle(const gli_t d, vector& w_wipeme, vecto return true; } -void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, - vector& w_wipeme, - vector& w_idleLinks, - vector& w_pendingSockets, - vector& w_unstableLinks, - vector& w_activeLinks) + +#if SRT_DEBUG_BONDING_STATES +class StabilityTracer +{ +public: + StabilityTracer() + { + } + + ~StabilityTracer() + { + srt::sync::ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint32_t activation_period_us, + int64_t stability_tmo_us, const std::string& state, uint16_t weight) + { + srt::sync::ScopedLock lck(m_mtx); + create_file(); + + m_fout << srt::sync::FormatTime(currtime) << ","; + m_fout << u.id() << ","; + m_fout << weight << ","; + m_fout << u.peerLatency_us() << ","; + m_fout << u.SRTT() << ","; + m_fout << u.RTTVar() << ","; + m_fout << stability_tmo_us << ","; + m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; + m_fout << state << ","; + m_fout << (srt::sync::is_zero(u.freshActivationStart()) ? -1 : (count_microseconds(currtime - u.freshActivationStart()))) << ","; + m_fout << activation_period_us << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + //srt::sync::ScopedLock lck(m_mtx); + m_fout << "Timepoint,SocketID,weight,usLatency,usRTT,usRTTVar,usStabilityTimeout,usSinceLastResp,State,usSinceActivation,usActivationPeriod\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "stability_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; +}; + +StabilityTracer s_stab_trace; +#endif + +void CUDTGroup::sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { // First, check status of every link - no matter if idle or active. for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) @@ -2887,36 +2970,39 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c HLOGC(gslog.Debug, log << "grp/sendBackup: socket in BROKEN state: @" << d->id << ", sockstatus=" << SockStatusStr(d->ps ? d->ps->getStatus() : SRTS_NONEXIST)); - w_wipeme.push_back(d->id); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_BROKEN, currtime); + w_sendBackupCtx.recordMemberState(&(*d), BKUPST_BROKEN); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(BKUPST_BROKEN), d->weight); +#endif continue; } if (d->sndstate == SRT_GST_IDLE) { - if (!send_CheckIdle(d, (w_wipeme), (w_pendingSockets))) - continue; + const BackupMemberState idle_state = sendBackup_QualifyIfStandBy(d); + sendBackup_AssignBackupState(d->ps->core(), idle_state, currtime); + w_sendBackupCtx.recordMemberState(&(*d), idle_state); - HLOGC(gslog.Debug, - log << "grp/sendBackup: socket in IDLE state: @" << d->id << " - will activate it IF NEEDED"); - // This is idle, we'll take care of them next time - // Might be that: - // - this socket is idle, while some NEXT socket is running - // - we need at least one running socket to work BEFORE activating the idle one. - // - if ALL SOCKETS ARE IDLE, then we simply activate the first from the list, - // and all others will be activated using the ISN from the first one. - w_idleLinks.push_back(d); - sendBackup_CheckIdleTime(d); + if (idle_state == BKUPST_STANDBY) + { + // TODO: Check if this is some abandoned logic. + sendBackup_CheckIdleTime(d); + } +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(idle_state), d->weight); +#endif continue; } if (d->sndstate == SRT_GST_RUNNING) { - if (!sendBackup_CheckRunningStability(d, (currtime))) - { - insert_uniq((w_unstableLinks), d); - } - // Unstable links should still be used for sending. - w_activeLinks.push_back(d); + const BackupMemberState active_state = sendBackup_QualifyActiveState(d, currtime); + sendBackup_AssignBackupState(d->ps->core(), active_state, currtime); + w_sendBackupCtx.recordMemberState(&(*d), active_state); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(active_state), d->weight); +#endif continue; } @@ -2924,7 +3010,56 @@ void CUDTGroup::sendBackup_QualifyMemberStates(const steady_clock::time_point& c log << "grp/sendBackup: socket @" << d->id << " not ready, state: " << StateStr(d->sndstate) << "(" << int(d->sndstate) << ") - NOT sending, SET AS PENDING"); - w_pendingSockets.push_back(d->id); + // Otherwise connection pending + sendBackup_AssignBackupState(d->ps->core(), BKUPST_PENDING, currtime); + w_sendBackupCtx.recordMemberState(&(*d), BKUPST_PENDING); +#if SRT_DEBUG_BONDING_STATES + s_stab_trace.trace(d->ps->core(), currtime, 0, 0, stateToStr(BKUPST_PENDING), d->weight); +#endif + } +} + + +void CUDTGroup::sendBackup_AssignBackupState(CUDT& sock, BackupMemberState state, const steady_clock::time_point& currtime) +{ + switch (state) + { + case BKUPST_PENDING: + case BKUPST_STANDBY: + case BKUPST_BROKEN: + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_FRESH: + if (is_zero(sock.freshActivationStart())) + { + sock.m_tsFreshActivation = currtime; + } + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point();; + break; + case BKUPST_ACTIVE_STABLE: + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsUnstableSince = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_UNSTABLE: + if (is_zero(sock.m_tsUnstableSince)) + { + sock.m_tsUnstableSince = currtime; + } + sock.m_tsFreshActivation = steady_clock::time_point(); + sock.m_tsWarySince = steady_clock::time_point(); + break; + case BKUPST_ACTIVE_UNSTABLE_WARY: + if (is_zero(sock.m_tsWarySince)) + { + sock.m_tsWarySince = currtime; + } + break; + default: + break; } } @@ -2935,7 +3070,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // buffer gets empty so that we can make sure that KEEPALIVE will be the // really last sent for longer time. CUDT& u = w_d->ps->core(); - if (is_zero(u.m_tsFreshActivation)) + if (is_zero(u.m_tsFreshActivation)) // TODO: Check if this condition is ever false return; CSndBuffer* b = u.m_pSndBuffer; @@ -2955,255 +3090,133 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) } } -#if SRT_DEBUG_BONDING_STATES -class StabilityTracer +// [[using locked(this->m_GroupLock)]] +CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_t d, const time_point currtime) { -public: - StabilityTracer() - { - } - - ~StabilityTracer() - { - srt::sync::ScopedLock lck(m_mtx); - m_fout.close(); - } - - void trace(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint32_t activation_period_us, - int64_t stability_tmo_us, const std::string& state, uint16_t weight) - { - srt::sync::ScopedLock lck(m_mtx); - create_file(); - - m_fout << srt::sync::FormatTime(currtime) << ","; - m_fout << u.id() << ","; - m_fout << weight << ","; - m_fout << u.peerLatency_us() << ","; - m_fout << u.SRTT() << ","; - m_fout << u.RTTVar() << ","; - m_fout << stability_tmo_us << ","; - m_fout << count_microseconds(currtime - u.lastRspTime()) << ","; - m_fout << state << ","; - m_fout << (srt::sync::is_zero(u.freshActivationStart()) ? -1 : (count_microseconds(currtime - u.freshActivationStart()))) << ","; - m_fout << activation_period_us << "\n"; - m_fout.flush(); - } - -private: - void print_header() - { - //srt::sync::ScopedLock lck(m_mtx); - m_fout << "Timepoint,SocketID,weight,usLatency,usRTT,usRTTVar,usStabilityTimeout,usSinceLastResp,State,usSinceActivation,usActivationPeriod\n"; - } - - void create_file() - { - if (m_fout) - return; - - std::string str_tnow = srt::sync::FormatTimeSys(srt::sync::steady_clock::now()); - str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part - while (str_tnow.find(':') != std::string::npos) { - str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); - } - const std::string fname = "stability_trace_" + str_tnow + ".csv"; - m_fout.open(fname, std::ofstream::out); - if (!m_fout) - std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + const CUDT& u = d->ps->core(); - print_header(); - } - -private: - srt::sync::Mutex m_mtx; - std::ofstream m_fout; -}; - -StabilityTracer s_stab_trace; -#endif - -/// TODO: Remove 'weight' parameter? Only needed for logging. -/// @retval 1 - link is identified as stable -/// @retval 0 - link state remains unchanged (too early to identify, still in activation phase) -/// @retval -1 - link is identified as unstable -static int sendBackup_CheckRunningLinkStable(const CUDT& u, const srt::sync::steady_clock::time_point& currtime, uint16_t weight ATR_UNUSED) -{ const uint32_t latency_us = u.peerLatency_us(); - const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. - const int64_t initial_stabtout_us = max(min_stability_us, latency_us); - const int64_t activation_period_us = initial_stabtout_us + 5 * CUDT::COMM_SYN_INTERVAL_US; + const int32_t min_stability_us = 60000; // Minimum Link Stability Timeout: 60ms. + const int64_t initial_stabtout_us = max(min_stability_us, latency_us); + const int64_t probing_period_us = initial_stabtout_us + 5 * CUDT::COMM_SYN_INTERVAL_US; - // RTT and RTTVar values are still being refined during activation period, - // therefore the dymanic timeout should not be used in activation phase. + // RTT and RTTVar values are still being refined during the probing period, + // therefore the dymanic timeout should not be used during the probing period. const bool is_activation_phase = !is_zero(u.freshActivationStart()) - && (count_microseconds(currtime - u.freshActivationStart()) <= activation_period_us); + && (count_microseconds(currtime - u.freshActivationStart()) <= probing_period_us); + // Initial stability timeout is used only in activation phase. + // Otherwise runtime stability is used, including the WARY state. const int64_t stability_tout_us = is_activation_phase ? initial_stabtout_us // activation phase : min(max(min_stability_us, 2 * u.SRTT() + 4 * u.RTTVar()), latency_us); - + const steady_clock::time_point last_rsp = max(u.freshActivationStart(), u.lastRspTime()); const steady_clock::duration td_response = currtime - last_rsp; + + // No response for a long time if (count_microseconds(td_response) > stability_tout_us) { -#if SRT_DEBUG_BONDING_STATES - s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION-UNSTABLE" : "UNSTABLE", weight); -#endif - return -1; + return BKUPST_ACTIVE_UNSTABLE; } - // u.lastRspTime() > currtime is alwats true due to the very first check above in this function -#if SRT_DEBUG_BONDING_STATES - s_stab_trace.trace(u, currtime, activation_period_us, stability_tout_us, is_activation_phase ? "ACTIVATION" : "STABLE", weight); -#endif - return is_activation_phase ? 0 : 1; -} + enterCS(u.m_StatsLock); + const int64_t drop_total = u.m_stats.sndDropTotal; + leaveCS(u.m_StatsLock); + const bool have_new_drops = d->pktSndDropTotal != drop_total; + if (have_new_drops) + { + d->pktSndDropTotal = drop_total; + if (!is_activation_phase) + return BKUPST_ACTIVE_UNSTABLE; + } -// [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckRunningStability(const gli_t d, const time_point currtime) -{ - CUDT& u = d->ps->core(); - // This link might be unstable, check its responsiveness status - // NOTE: currtime - last_rsp_time: we believe this value will be always positive as - // the Tk clock is believed to be monotonic. The resulting value - - // IMPORTANT: the socket could be potentially updated IN THE MEANTIME in another - // thread AFTER (!!!) currtime has been read, but BEFORE (!!!) this value us used - // for calculation - which could make the difference negative. - // There's no way to avoid it because it would require making a mutex-locking for - // updating the m_tsLastRspTime field. This is useless because avoiding the - // negative value is relatively easy, while introducing a mutex would only add a - // deadlock risk and performance degradation. + // Responsive: either stable, wary or still fresh activated. + if (is_activation_phase) + return BKUPST_ACTIVE_FRESH; - HLOGC(gslog.Debug, - log << "grp/sendBackup: CHECK STABLE: @" << d->id - << ": TIMEDIFF {response= " << FormatDuration(currtime - u.m_tsLastRspTime) - << " ACK=" << FormatDuration(currtime - u.m_tsLastRspAckTime) << " activation=" - << (!is_zero(u.m_tsFreshActivation) ? FormatDuration(currtime - u.m_tsFreshActivation) : "PAST") - << " unstable=" - << (!is_zero(u.m_tsUnstableSince) ? FormatDuration(currtime - u.m_tsUnstableSince) : "NEVER") - << "}"); + const bool is_wary = !is_zero(u.m_tsWarySince); + const bool is_wary_probing = is_wary + && (count_microseconds(currtime - u.m_tsWarySince) <= 4 * u.peerLatency_us()); - const int is_stable = sendBackup_CheckRunningLinkStable(u, currtime, d->weight); + const bool is_unstable = !is_zero(u.m_tsUnstableSince); - if (is_stable >= 0) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link STABLE: @" << d->id - << (!is_zero(u.m_tsUnstableSince) ? " - RESTORED" : " - CONTINUED") - << ", state RUNNING - will send a payload"); + // If unstable and not in wary, become wary. + if (is_unstable && !is_wary) + return BKUPST_ACTIVE_UNSTABLE_WARY; - u.m_tsUnstableSince = steady_clock::time_point(); + // Still probing for stability. + if (is_wary_probing) + return BKUPST_ACTIVE_UNSTABLE_WARY; - // For some cases - if (is_stable > 0) - u.m_tsFreshActivation = steady_clock::time_point(); - } - else + if (is_wary) { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link UNSTABLE for " << FormatDuration(currtime - u.m_tsUnstableSince) << " : @" - << d->id << " - will send a payload"); - if (is_zero(u.m_tsUnstableSince)) - { - u.m_tsUnstableSince = currtime; - } + LOGC(gslog.Debug, + log << "grp/sendBackup: @" << u.id() << " wary->stable after " << count_milliseconds(currtime - u.m_tsWarySince) << " ms"); } - return is_stable >= 0; + return BKUPST_ACTIVE_STABLE; } // [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckSendStatus(gli_t d, - const steady_clock::time_point& currtime ATR_UNUSED, - const int stat, - const int erc, - const int32_t lastseq, - const int32_t pktseq, - CUDT& w_u, - int32_t& w_curseq, - vector& w_parallel, - int& w_final_stat, - uint16_t& w_maxActiveWeight, - size_t& w_nsuccessful, - bool& w_is_unstable) +bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime ATR_UNUSED, + const int send_status, + const int32_t lastseq, + const int32_t pktseq, + CUDT& w_u, + int32_t& w_curseq, + int& w_final_stat) { - bool none_succeeded = true; + if (send_status == -1) + return false; // Sending failed. + - // sending over this socket has succeeded - if (stat != -1) + bool send_succeeded = false; + if (w_curseq == SRT_SEQNO_NONE) { - if (w_curseq == SRT_SEQNO_NONE) - { - w_curseq = pktseq; - } - else if (w_curseq != lastseq) - { - // We believe that all running links use the same seq. - // But we can do some sanity check. - LOGC(gslog.Error, - log << "grp/sendBackup: @" << w_u.m_SocketID << ": IPE: another running link seq discrepancy: %" - << lastseq << " vs. previous %" << w_curseq << " - fixing"); - - // Override must be done with a sequence number greater by one. - - // Example: - // - // Link 1 before sending: curr=1114, next=1115 - // After sending it reports pktseq=1115 - // - // Link 2 before sending: curr=1110, next=1111 (->lastseq before sending) - // THIS CHECK done after sending: - // -- w_curseq(1115) != lastseq(1111) - // - // NOW: Link 1 after sending is: - // curr=1115, next=1116 - // - // The value of w_curseq here = 1115, while overrideSndSeqNo - // calls setInitialSndSeq(seq), which sets: - // - curr = seq - 1 - // - next = seq - // - // So, in order to set curr=1115, next=1116 - // this must set to 1115+1. - - w_u.overrideSndSeqNo(CSeqNo::incseq(w_curseq)); - } + w_curseq = pktseq; + } + else if (w_curseq != lastseq) + { + // We believe that all active links use the same seq. + // But we can do some sanity check. + LOGC(gslog.Error, + log << "grp/sendBackup: @" << w_u.m_SocketID << ": IPE: another running link seq discrepancy: %" + << lastseq << " vs. previous %" << w_curseq << " - fixing"); - // If this link is already found as unstable, - // do not add it to the "w_parallel", as all links out - // of these "w_parallels" will be later tried to be - // shrunk to 1. Out of all links currently running we need - // only 1 link STABLE, and we allow any nymber of unstable - // links. + // Override must be done with a sequence number greater by one. - if (is_zero(w_u.m_tsUnstableSince)) - { - w_parallel.push_back(d); - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: Link @" << w_u.m_SocketID << " still UNSTABLE for " - << FormatDuration(currtime - w_u.m_tsUnstableSince) << ", not counting as w_parallel"); - } + // Example: + // + // Link 1 before sending: curr=1114, next=1115 + // After sending it reports pktseq=1115 + // + // Link 2 before sending: curr=1110, next=1111 (->lastseq before sending) + // THIS CHECK done after sending: + // -- w_curseq(1115) != lastseq(1111) + // + // NOW: Link 1 after sending is: + // curr=1115, next=1116 + // + // The value of w_curseq here = 1115, while overrideSndSeqNo + // calls setInitialSndSeq(seq), which sets: + // - curr = seq - 1 + // - next = seq + // + // So, in order to set curr=1115, next=1116 + // this must set to 1115+1. - // State it as succeeded, though. We don't know if the link - // is broken until we get the connection broken confirmation, - // and the instability state may wear off next time. - none_succeeded = false; - w_final_stat = stat; - ++w_nsuccessful; - w_maxActiveWeight = max(w_maxActiveWeight, d->weight); - } - else if (erc == SRT_EASYNCSND) - { - HLOGC(gslog.Debug, log << "grp/sendBackup: Link @" << w_u.m_SocketID << " DEEMED UNSTABLE (not ready to send)"); - w_is_unstable = true; + w_u.overrideSndSeqNo(CSeqNo::incseq(w_curseq)); } - return none_succeeded; + // State it as succeeded, though. We don't know if the link + // is broken until we get the connection broken confirmation, + // and the instability state may wear off next time. + send_succeeded = true; + w_final_stat = send_status; + + return send_succeeded; } // [[using locked(this->m_GroupLock)]] @@ -3257,85 +3270,64 @@ void CUDTGroup::sendBackup_Buffering(const char* buf, const int len, int32_t& w_ m_iLastSchedSeqNo = oldest_buffer_seq; } -bool CUDTGroup::sendBackup_IsActivationNeeded(const vector& idleLinks, - const vector& unstableLinks, - const vector& activeLinks, - const uint16_t maxActiveWeight, - string& activate_reason ATR_UNUSED) const +size_t CUDTGroup::sendBackup_TryActivateStandbyIfNeeded( + const char* buf, + const int len, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + int32_t& w_curseq, + int32_t& w_final_stat, + SendBackupCtx& w_sendBackupCtx, + CUDTException& w_cx, + const steady_clock::time_point& currtime) { - SRT_ASSERT(activeLinks.size() >= unstableLinks.size()); - bool need_activate = activeLinks.size() <= unstableLinks.size(); // <= for sanity, should be just == - IF_HEAVY_LOGGING(activate_reason = "BY NO REASON???"); - if (need_activate) + const unsigned num_standby = w_sendBackupCtx.countMembersByState(BKUPST_STANDBY); + if (num_standby == 0) { - HLOGC(gslog.Debug, - log << "grp/sendBackup: all " << activeLinks.size() << " links unstable - will activate an idle link"); - IF_HEAVY_LOGGING(activate_reason = "no stable links"); + return 0; } - else if (activeLinks.empty() || - (!idleLinks.empty() && idleLinks[0]->weight > maxActiveWeight)) + + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_FRESH); + const unsigned num_fresh = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + + if (num_stable + num_fresh == 0) { - need_activate = true; -#if ENABLE_HEAVY_LOGGING - if (activeLinks.empty()) - { - activate_reason = "no successful links found"; - LOGC(gslog.Debug, log << "grp/sendBackup: no active links were successful - will activate an idle link"); - } - else if (idleLinks.empty()) - { - // This should be impossible. - activate_reason = "WEIRD (no idle links!)"; - LOGC(gslog.Debug, - log << "grp/sendBackup: BY WEIRD AND IMPOSSIBLE REASON (IPE?) - will activate an idle link"); - } - else - { - LOGC(gslog.Debug, - log << "grp/sendBackup: found link weight " << idleLinks[0]->weight << " PREF OVER " - << maxActiveWeight << " (highest from active links) - will activate an idle link"); - activate_reason = "found higher weight link"; - } -#endif + LOGC(gslog.Warn, + log << "grp/sendBackup: trying to activate a stand-by link (" << num_standby << " available). " + << "Reason: no stable links" + ); + } + else if (w_sendBackupCtx.maxActiveWeight() < w_sendBackupCtx.maxStandbyWeight()) + { + LOGC(gslog.Warn, + log << "grp/sendBackup: trying to activate a stand-by link (" << num_standby << " available). " + << "Reason: max active weight " << w_sendBackupCtx.maxActiveWeight() + << ", max stand by weight " << w_sendBackupCtx.maxStandbyWeight() + ); } else { - HLOGC(gslog.Debug, - log << "grp/sendBackup: max active weight " << maxActiveWeight << ",: " - << " first idle wight: " << (idleLinks.size() > 0 ? int(idleLinks[0]->weight) : -1) - << " - will NOT activate an idle link"); + /*LOGC(gslog.Warn, + log << "grp/sendBackup: no need to activate (" << num_standby << " available). " + << "Max active weight " << w_sendBackupCtx.maxActiveWeight() + << ", max stand by weight " << w_sendBackupCtx.maxStandbyWeight() + );*/ + return 0; } - return need_activate; -} - -// [[using locked(this->m_GroupLock)]] -size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& idleLinks, - const char* buf, - const int len, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - int32_t& w_curseq, - int32_t& w_final_stat, - CUDTException& w_cx, - vector& w_parallel, - vector& w_wipeme, - const string& activate_reason ATR_UNUSED) -{ int stat = -1; - // If we have no stable links, activate one of idle links. + size_t num_activated = 0; - HLOGC(gslog.Debug, - log << "grp/sendBackup: " << activate_reason << ", trying to activate an idle link (" << idleLinks.size() - << " available)"); - - size_t nactive = 0; - - for (vector::const_iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) + w_sendBackupCtx.sortByWeightAndState(); + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) { + if (member->state != BKUPST_STANDBY) + continue; + int erc = 0; - gli_t d = *i; + SocketData* d = member->pSocketData; // Now send and check the status // The link could have got broken @@ -3350,7 +3342,7 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i // situation of sending the very first packet after connection. HLOGC(gslog.Debug, - log << "grp/sendBackup: ... trying @" << d->id << " - sending the VERY FIRST message"); + log << "grp/sendBackup: ... trying @" << d->id << " - sending the VERY FIRST message"); stat = d->ps->core().sendmsg2(buf, len, (w_mc)); if (stat != -1) @@ -3365,13 +3357,13 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i else { HLOGC(gslog.Debug, - log << "grp/sendBackup: ... trying @" << d->id << " - resending " << m_SenderBuffer.size() - << " collected messages..."); + log << "grp/sendBackup: ... trying @" << d->id << " - resending " << m_SenderBuffer.size() + << " collected messages..."); // Note: this will set the currently required packet // because it has been just freshly added to the sender buffer stat = sendBackupRexmit(d->ps->core(), (w_mc)); } - ++nactive; + ++num_activated; } catch (CUDTException& e) { @@ -3379,43 +3371,24 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i // but that's ok - we want this sending interrupted even in half. w_cx = e; stat = -1; - erc = e.getErrorCode(); + erc = e.getErrorCode(); } - d->sndresult = stat; + d->sndresult = stat; d->laststatus = d->ps->getStatus(); if (stat != -1) { - if (d->sndstate != SRT_GST_RUNNING) - { - steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsFreshActivation = currtime; - HLOGC(gslog.Debug, - log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno - << " LINK ACTIVATED (pri: " << d->weight << ")."); - } - else - { - LOGC(gslog.Warn, - log << "@" << d->id << ":... sending SUCCESSFUL #" << w_mc.msgno - << " LINK ACTIVATED (pri: " << d->weight << ")."); - } + d->sndstate = SRT_GST_RUNNING; + sendBackup_AssignBackupState(d->ps->core(), BKUPST_ACTIVE_FRESH, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_ACTIVE_FRESH); // Note: this will override the sequence number // for all next iterations in this loop. - d->sndstate = SRT_GST_RUNNING; - - if (is_zero(d->ps->core().m_tsUnstableSince)) - { - w_parallel.push_back(d); - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: Link @" << d->id << " (idle) UNSTABLE, not counting as parallel"); - } w_none_succeeded = false; - w_final_stat = stat; + w_final_stat = stat; + + LOGC(gslog.Warn, + log << "@" << d->id << " FRESH-ACTIVATED"); // We've activated the link, so that's enough. break; @@ -3426,29 +3399,30 @@ size_t CUDTGroup::sendBackup_TryActivateIdleLink(const vector& i if (erc != SRT_EASYNCSND) { isblocked = false; - w_wipeme.push_back(d->id); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_BROKEN, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_BROKEN); } // If we found a blocked link, leave it alone, however // still try to send something over another link - HLOGC(gslog.Debug, - log << "@" << d->id << " FAILED (" << (isblocked ? "blocked" : "ERROR") - << "), trying to activate another link."); + LOGC(gslog.Warn, + log << "@" << d->id << " FAILED (" << (isblocked ? "blocked" : "ERROR") + << "), trying to activate another link."); } - return nactive; + return num_activated; } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets, vector& w_wipeme) +void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { - if (pendingSockets.empty()) + if (w_sendBackupCtx.countMembersByState(BKUPST_PENDING) == 0) return; - HLOGC(gslog.Debug, log << "grp/send*: found pending sockets, polling them."); + HLOGC(gslog.Debug, log << "grp/send*: checking pending sockets."); - // These sockets if they are in pending state, they should be added to m_SndEID + // These sockets if they are in pending state, should be added to m_SndEID // at the connecting stage. CEPoll::fmap_t sready; @@ -3456,49 +3430,97 @@ void CUDTGroup::send_CheckPendingSockets(const vector& pendingSockets { // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); - copy(pendingSockets.begin(), pendingSockets.end(), back_inserter(w_wipeme)); + return; } - else + { - { - InvertedLock ug(m_GroupLock); - m_pGlobal->m_EPoll.swait( - *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened - } + InvertedLock ug(m_GroupLock); + m_pGlobal->m_EPoll.swait( + *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything has happened + } - if (m_bClosing) - { - HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - } + if (m_bClosing) + { + HLOGC(gslog.Debug, log << "grp/send...: GROUP CLOSED, ABANDONING"); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } - // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + // Some sockets could have been closed in the meantime. + if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_PENDING) + continue; + + const SRTSOCKET sockid = member->pSocketData->id; + if (!CEPoll::isready(sready, sockid, SRT_EPOLL_ERR)) + continue; + + HLOGC(gslog.Debug, log << "grp/send*: Socket @" << sockid << " reported FAILURE - qualifying as broken."); + w_sendBackupCtx.updateMemberState(member->pSocketData, BKUPST_BROKEN); + if (member->pSocketData->ps) + sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); + + const int no_events = 0; + m_pGlobal->m_EPoll.update_usock(m_SndEID, sockid, &no_events); + } + + // After that, all sockets that have been reported + // as ready to write should be removed from EID. This + // will also remove those sockets that have been added + // as redundant links at the connecting stage and became + // writable (connected) before this function had a chance + // to check them. + m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); +} + +// [[using locked(this->m_GroupLock)]] +void CUDTGroup::sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) +{ + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + if (num_stable == 0) + return; + + const unsigned num_unstable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE); + const unsigned num_wary = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE_WARY); + if (num_unstable + num_wary == 0) + return; + HLOGC(gslog.Debug, log << "grp/send*: checking unstable sockets."); - HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_ACTIVE_UNSTABLE && member->state != BKUPST_ACTIVE_UNSTABLE_WARY) + continue; - // sockets in EX: should be moved to w_wipeme. - for (vector::const_iterator i = pendingSockets.begin(); i != pendingSockets.end(); ++i) + CUDT& sock = member->pSocketData->ps->core(); + + if (is_zero(sock.m_tsUnstableSince)) { - if (CEPoll::isready(sready, *i, SRT_EPOLL_ERR)) - { - HLOGC(gslog.Debug, log << "grp/send*: Socket @" << (*i) << " reported FAILURE - moved to wiped."); - // Failed socket. Move d to w_wipeme. Remove from eid. - w_wipeme.push_back(*i); - int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); - } + LOGC(gslog.Error, log << "grp/send* IPE: Socket @" << member->socketID + << " is qualified as unstable, but does not have the 'unstable since' timestamp. Still marking for closure."); } - // After that, all sockets that have been reported - // as ready to write should be removed from EID. This - // will also remove those sockets that have been added - // as redundant links at the connecting stage and became - // writable (connected) before this function had a chance - // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); + const int unstable_for_ms = count_milliseconds(currtime - sock.m_tsUnstableSince); + if (unstable_for_ms < sock.peerIdleTimeout_ms()) + continue; + + // Requesting this socket to be broken with the next CUDT::checkExpTimer() call. + sock.breakAsUnstable(); + + LOGC(gslog.Warn, log << "grp/send*: Socket @" << member->socketID << " is unstable for " << unstable_for_ms + << "ms - requesting breakage."); + + //w_sendBackupCtx.updateMemberState(member->pSocketData, BKUPST_BROKEN); + //if (member->pSocketData->ps) + // sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); } } @@ -3538,6 +3560,45 @@ void CUDTGroup::send_CloseBrokenSockets(vector& w_wipeme) w_wipeme.clear(); } +// [[using locked(this->m_GroupLock)]] +void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) +{ + if (w_sendBackupCtx.countMembersByState(BKUPST_BROKEN) == 0) + return; + + InvertedLock ug(m_GroupLock); + + // With unlocked GroupLock, we can now lock GlobControlLock. + // This is needed prevent any of them be deleted from the container + // at the same time. + ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (member->state != BKUPST_BROKEN) + continue; + + // m_GroupLock is unlocked, therefore member->pSocketData can't be used. + const SRTSOCKET sockid = member->socketID; + CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(sockid); + + // If the socket has been just moved to ClosedSockets, it means that + // the object still exists, but it will be no longer findable. + if (!s) + continue; + + LOGC(gslog.Debug, + log << "grp/send...: BROKEN SOCKET @" << sockid << " - CLOSING, to be removed from group."); + + // As per sending, make it also broken so that scheduled + // packets will be also abandoned. + s->setBrokenClosed(); + } + + // TODO: all broken members are to be removed from the context now??? +} + struct FByOldestActive { typedef CUDTGroup::gli_t gli_t; @@ -3551,25 +3612,12 @@ struct FByOldestActive }; // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, - vector& w_parallel, +void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx, int& w_final_stat, bool& w_none_succeeded, SRT_MSGCTRL& w_mc, CUDTException& w_cx) { -#if ENABLE_HEAVY_LOGGING - // Potential problem to be checked in developer mode - for (vector::iterator p = w_parallel.begin(); p != w_parallel.end(); ++p) - { - if (std::find(unstableLinks.begin(), unstableLinks.end(), *p) != unstableLinks.end()) - { - LOGC(gslog.Debug, - log << "grp/sendBackup: IPE: parallel links enclose unstable link @" << (*p)->ps->m_SocketID); - } - } -#endif - // In contradiction to broadcast sending, backup sending must check // the blocking state in total first. We need this information through // epoll because we didn't use all sockets to send the data hence the @@ -3588,12 +3636,15 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // over any link (hence "none succeeded"), but there are some unstable // links and no parallel links. We need to WAIT for any of the links // to become available for sending. - if (!w_parallel.empty() || unstableLinks.empty() || !w_none_succeeded) - return; + // Note: A link is added in unstableLinks if sending has failed with SRT_ESYNCSND. + const unsigned num_unstable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE); + const unsigned num_wary = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_UNSTABLE_WARY); + if ((num_unstable + num_wary == 0) || !w_none_succeeded) + return; - HLOGC(gslog.Debug, log << "grp/sendBackup: no parallel links and " - << unstableLinks.size() << " unstable links - checking..."); + HLOGC(gslog.Debug, log << "grp/sendBackup: no successfull sending: " + << (num_unstable + num_wary) << " unstable links - waiting to retry sending..."); // Note: GroupLock is set already, skip locks and checks getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); @@ -3630,7 +3681,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // Simply notify all dead links, regardless as to whether the number // of group members decreases below. If the number of corpses reaches // this number, consider the group connection broken. - size_t nlinks = m_Group.size(); + const size_t nlinks = m_Group.size(); size_t ndead = 0; RetryWaitBlocked: @@ -3714,8 +3765,13 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, int stat = -1; for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) { - // int erc = 0; - + // We are waiting only for active members + if (d->sndstate != SRT_GST_RUNNING) + { + HLOGC(gslog.Debug, + log << "grp/sendBackup: member @" << d->id << " state is not RUNNING - SKIPPING from retry/waiting"); + continue; + } // Skip if not writable in this run if (!CEPoll::isready(sready, d->id, SRT_EPOLL_OUT)) { @@ -3724,13 +3780,6 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, continue; } - if (d->sndstate == SRT_GST_RUNNING) - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: link @" << d->id << " RUNNING - SKIPPING from activate and resend"); - continue; - } - try { // Note: this will set the currently required packet @@ -3744,7 +3793,6 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, // but that's ok - we want this sending interrupted even in half. w_cx = e; stat = -1; - // erc = e.getErrorCode(); } d->sndresult = stat; @@ -3756,12 +3804,12 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, continue; } - w_parallel.push_back(d); w_final_stat = stat; - steady_clock::time_point currtime = steady_clock::now(); - d->ps->core().m_tsFreshActivation = currtime; d->sndstate = SRT_GST_RUNNING; w_none_succeeded = false; + const steady_clock::time_point currtime = steady_clock::now(); + sendBackup_AssignBackupState(d->ps->core(), BKUPST_ACTIVE_UNSTABLE_WARY, currtime); + w_sendBackupCtx.updateMemberState(&(*d), BKUPST_ACTIVE_UNSTABLE_WARY); HLOGC(gslog.Debug, log << "grp/sendBackup: after waiting, ACTIVATED link @" << d->id); break; @@ -3775,76 +3823,76 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(const vector& unstableLinks, HLOGC(gslog.Debug, log << "grp/sendBackup: still have " << nwaiting << " waiting and none succeeded, REPEAT"); goto RetryWaitBlocked; } - - HLOGC(gslog.Debug, log << "grp/sendBackup: " << nactivated << " links activated with " - << unstableLinks.size() << " unstable"); } // [[using locked(this->m_GroupLock)]] -void CUDTGroup::sendBackup_SilenceRedundantLinks(vector& w_parallel) +void CUDTGroup::sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime) { // The most important principle is to keep the data being sent constantly, - // even if it means temporarily full redundancy. However, if you are certain - // that you have multiple stable links running at the moment, SILENCE all but - // the one with highest priority. - if (w_parallel.size() <= 1) + // even if it means temporarily full redundancy. + // A member can be silenced only if there is at least one stable memebr. + const unsigned num_stable = w_sendBackupCtx.countMembersByState(BKUPST_ACTIVE_STABLE); + if (num_stable == 0) return; - sort(w_parallel.begin(), w_parallel.end(), FPriorityOrder()); - steady_clock::time_point currtime = steady_clock::now(); + // INPUT NEEDED: + // - stable member with maximum weight - vector::iterator b = w_parallel.begin(); + uint16_t max_weight_stable = 0; + SRTSOCKET stableSocketId = SRT_INVALID_SOCK; // SocketID of a stable link with higher weight + + w_sendBackupCtx.sortByWeightAndState(); + //LOGC(gslog.Debug, log << "grp/silenceRedundant: links after sort: " << w_sendBackupCtx.printMembers()); + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (!isStateActive(member->state)) + continue; - // Additional criterion: if you have multiple links with the same weight, - // check if you have at least one with m_tsTmpActiveSince == 0. If not, - // sort them additionally by this time. + const bool haveHigherWeightStable = stableSocketId != SRT_INVALID_SOCK; + const uint16_t weight = member->pSocketData->weight; - vector::iterator b1 = b, e = ++b1; + if (member->state == BKUPST_ACTIVE_STABLE) + { + // silence stable link if it is not the first stable + if (!haveHigherWeightStable) + { + max_weight_stable = (int) weight; + stableSocketId = member->socketID; + continue; + } + else + { + LOGC(gslog.Note, log << "grp/sendBackup: silencing stable member @" << member->socketID << " (weight " << weight + << ") in favor of @" << stableSocketId << " (weight " << max_weight_stable << ")"); + } + } + else if (haveHigherWeightStable && weight <= max_weight_stable) + { + LOGC(gslog.Note, log << "grp/sendBackup: silencing member @" << member->socketID << " (weight " << weight + << " " << stateToStr(member->state) + << ") in favor of @" << stableSocketId << " (weight " << max_weight_stable << ")"); + } + else + { + continue; + } - // Both e and b1 stand on b+1 position. - // We have a guarantee that b+1 still points to a valid element. - while (e != w_parallel.end()) - { - if ((*e)->weight != (*b)->weight) - break; - ++e; - } + // TODO: Move to a separate function sendBackup_SilenceMember + SocketData* d = member->pSocketData; + CUDT& u = d->ps->core(); - if (b1 != e) - { - // More than 1 link with the same weight. Sorting them according - // to a different criterion will not change the previous sorting order - // because the elements in this range are equal according to the previous - // criterion. - // Here find the link with least time. The "trap" zero time matches this - // requirement, occasionally. - sort(b, e, FByOldestActive()); - } + sendBackup_AssignBackupState(u, BKUPST_STANDBY, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_STANDBY); - // After finding the link to leave active, leave it behind. - HLOGC(gslog.Debug, log << "grp/sendBackup: keeping parallel link @" << (*b)->id << " and silencing others:"); - ++b; - for (; b != w_parallel.end(); ++b) - { - gli_t& d = *b; if (d->sndstate != SRT_GST_RUNNING) { LOGC(gslog.Error, - log << "grp/sendBackup: IPE: parallel link container contains non-running link @" << d->id); - continue; - } - CUDT& ce = d->ps->core(); - if (!is_zero(ce.m_tsFreshActivation) && sendBackup_CheckRunningLinkStable(ce, currtime, d->weight) != 1) - { - HLOGC(gslog.Debug, - log << "... not silencing @" << d->id << ": too early"); + log << "grp/sendBackup: IPE: misidentified a non-running link @" << d->id << " as active"); continue; } - // Clear activation time because the link is no longer active! d->sndstate = SRT_GST_IDLE; - HLOGC(gslog.Debug, log << " ... @" << d->id << " ACTIVATED: " << FormatTime(ce.m_tsFreshActivation)); - ce.m_tsFreshActivation = steady_clock::time_point(); } } @@ -3864,21 +3912,6 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // [[using assert(this->m_pSndBuffer != nullptr)]]; - // NOTE: This is a "vector of list iterators". Every element here - // is an iterator to another container. - // Note that "list" is THE ONLY container in standard C++ library, - // for which NO ITERATORS ARE INVALIDATED after a node at particular - // iterator has been removed, except for that iterator itself. - vector wipeme; - vector idleLinks; - vector pendingSockets; - vector activeLinks; // All non-idle and non-pending links - vector unstableLinks; // Active, but unstable. - - int stat = 0; - int final_stat = -1; - SRT_ATR_UNUSED CUDTException cx(MJ_SUCCESS, MN_NONE, 0); - // First, acquire GlobControlLock to make sure all member sockets still exist enterCS(m_pGlobal->m_GlobControlLock); ScopedLock guard(m_GroupLock); @@ -3895,130 +3928,46 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) steady_clock::time_point currtime = steady_clock::now(); - activeLinks.reserve(m_Group.size()); - - // Qualify states of member links - sendBackup_QualifyMemberStates(currtime, (wipeme), (idleLinks), (pendingSockets), (unstableLinks), (activeLinks)); - - // Sort the idle sockets by priority so the highest priority idle links are checked first. - sort(idleLinks.begin(), idleLinks.end(), FPriorityOrder()); - -#if ENABLE_HEAVY_LOGGING - { - vector show_running, show_idle; - for (vector::iterator i = activeLinks.begin(); i != activeLinks.end(); ++i) - show_running.push_back((*i)->id); - - for (vector::iterator i = idleLinks.begin(); i != idleLinks.end(); ++i) - show_idle.push_back((*i)->id); - - LOGC(gslog.Debug, - log << "grp/sendBackup: RUNNING: " << PrintableMod(show_running, "@") - << " IDLE: " << PrintableMod(show_idle, "@")); - } -#endif - - // Ok, we've separated the unstable from activeLinks just to know if: - // - we have any STABLE activeLinks (if not, we must activate a backup link) - // - we have multiple stable activeLinks and we need to stop all but one - - // Normally there should be only one link with state == SRT_GST_RUNNING, but there might - // be multiple links set as running when a "breaking suspection" is set on a link. - - bool none_succeeded = true; // be pessimistic + SendBackupCtx sendBackupCtx; // default initialized as empty + // TODO: reserve? sendBackupCtx.memberStates.reserve(m_Group.size()); - // All sockets that are currently stable and sending was successful - // should be added to the vector. Later, all but the one with highest - // priority should remain active. - vector parallel; + sendBackup_QualifyMemberStates((sendBackupCtx), currtime); int32_t curseq = SRT_SEQNO_NONE; size_t nsuccessful = 0; - // Maximum weight of active links. - uint16_t maxActiveWeight = 0; - if (w_mc.srctime == 0) - w_mc.srctime = count_microseconds(steady_clock::now().time_since_epoch()); + SRT_ATR_UNUSED CUDTException cx(MJ_SUCCESS, MN_NONE, 0); // TODO: Delete then? + uint16_t maxActiveWeight = 0; // Maximum weight of active links. + // The number of bytes sent or -1 for error will be stored in group_send_result + int group_send_result = sendBackup_SendOverActive(buf, len, w_mc, currtime, (curseq), (nsuccessful), (maxActiveWeight), (sendBackupCtx), (cx)); + bool none_succeeded = (nsuccessful == 0); - // We believe that we need to send the payload over every activeLinks link anyway. - for (vector::iterator snd = activeLinks.begin(); snd != activeLinks.end(); ++snd) - { - gli_t d = *snd; - int erc = 0; // success - // Remaining sndstate is SRT_GST_RUNNING. Send a payload through it. - CUDT& u = d->ps->core(); - int32_t lastseq = u.schedSeqNo(); - try - { - // This must be wrapped in try-catch because on error it throws an exception. - // Possible return values are only 0, in case when len was passed 0, or a positive - // >0 value that defines the size of the data that it has sent, that is, in case - // of Live mode, equal to 'len'. - stat = u.sendmsg2(buf, len, (w_mc)); - } - catch (CUDTException& e) - { - cx = e; - stat = -1; - erc = e.getErrorCode(); - } + // Save current payload in group's sender buffer. + sendBackup_Buffering(buf, len, (curseq), (w_mc)); - bool is_unstable = false; - none_succeeded &= sendBackup_CheckSendStatus(d, - currtime, - stat, - erc, - lastseq, - w_mc.pktseq, - (u), - (curseq), - (parallel), - (final_stat), - (maxActiveWeight), - (nsuccessful), - (is_unstable)); - - // TODO: Wasn't it done in sendBackup_QualifyMemberStates()? Sanity check? - if (is_unstable && is_zero(u.m_tsUnstableSince)) // Add to unstable only if it wasn't unstable already - insert_uniq((unstableLinks), d); + sendBackup_TryActivateStandbyIfNeeded(buf, len, (none_succeeded), + (w_mc), + (curseq), + (group_send_result), + (sendBackupCtx), + (cx), currtime); - d->sndresult = stat; - d->laststatus = d->ps->getStatus(); - } + sendBackup_CheckPendingSockets((sendBackupCtx), currtime); + sendBackup_CheckUnstableSockets((sendBackupCtx), currtime); - // Ok, we have attempted to send a payload over all active links - // We know that at least one is successful if we have non-default curmsgno - // value. + //LOGC(gslog.Debug, log << "grp/sendBackup: links after all checks: " << sendBackupCtx.printMembers()); - // Now we need to check the link that is currently defined as - // main active because we can have: - // - one active link only - we want to check its status - // - two active links - one "main active" and one "temporarily - // activated" + // Re-check after the waiting lock has been reacquired + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - // Here the only thing we need to decide about is: - // 1. if we have at least one active and STABLE link - // - if there are no stable links, activate one idle link - // 2. if we have more than one active and stable link - // - select those with highest priority out of them - // - select the first in order from those - // - silence the rest (turn them idle) + sendBackup_CloseBrokenSockets((sendBackupCtx)); - // In Backup group, we have the following possibilities - // - only one link active and stable (normal) - // - multiple links active (and possibly one of them stable) - // - // We might have had a situation that sending was not possible - // due to have been blocked. - // - // If you have any link blocked, treat it as unstable, which - // means that one link out of the waiting idle must be activated. - // - // HOWEVER: - // - // Collect blocked links in order to make an additional check: - // - // If all links out of the unstable-running links are blocked, + // Re-check after the waiting lock has been reacquired + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + // If all links out of the unstable-running links are blocked (SRT_EASYNCSND), // perform epoll wait on them. In this situation we know that // there are no idle blocked links because IDLE LINK CAN'T BE BLOCKED, // no matter what. It's because the link may only be blocked if @@ -4028,87 +3977,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // This means that in case when we have no stable links, we // need to try out any link that can accept the rexmit-load. // We'll check link stability at the next sending attempt. + sendBackup_RetryWaitBlocked((sendBackupCtx), (group_send_result), (none_succeeded), (w_mc), (cx)); - // Here we need to activate one IDLE link, if we have - // no stable links. - // Some portion of logical exclusions: - // - // - sockets that were broken in the beginning are already wiped out - // - broken sockets are checked first, so they can't be simultaneously idle - // - idle sockets can't get broken because there's no operation done on them - // - running sockets are the only one that could change sndstate here - // - running sockets can either remain running or turn to broken - // In short: Running and Broken sockets can't become idle, - // although Running sockets can become Broken. - - // There's no certainty here as to whether at least one link was - // running and it has successfully performed the operation. - // Might have even happened that we had 2 running links that - // got broken and 3 other links so far in idle sndstate that just connected - // at that very moment (in Backup group: 1 running stable, 1 running - // unstable, 3 links keeping connetion being idle). - - // In this case we have 3 idle links to activate one of, - // but there is no message number base. If so, take the number for - // the first activated link as a good deal. - // - // If it has additionally happened that the first link got broken at - // that very moment of sending, the second one has a chance to succeed - // and therefore take over the leading role in setting the leading message - // number. If the second one fails, too, then the only remaining idle link - // will simply go with its own original message number. - // - // Now we can go to the idle links and attempt to send the payload - // also over them. - - sendBackup_Buffering(buf, len, (curseq), (w_mc)); - - string activate_reason; - if (sendBackup_IsActivationNeeded(idleLinks, unstableLinks, activeLinks, maxActiveWeight, activate_reason)) - { - if (idleLinks.empty()) - { - HLOGP(gslog.Debug, "grp/sendBackup: no idle links to activate, keeping only unstable links"); - } - else - { - const size_t n ATR_UNUSED = sendBackup_TryActivateIdleLink(idleLinks, - buf, - len, - (none_succeeded), - (w_mc), - (curseq), - (final_stat), - (cx), - (parallel), - (wipeme), - activate_reason); - - HLOGC(gslog.Debug, log << "grp/sendBackup: activated " << n << " idle links tried"); - } - } - else - { - HLOGC(gslog.Debug, - log << "grp/sendBackup: have activeLinks links, stable=" << (activeLinks.size() - unstableLinks.size()) - << " unstable=" << unstableLinks.size()); - } - - send_CheckPendingSockets(pendingSockets, (wipeme)); - - // Re-check after the waiting lock has been reacquired - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - send_CloseBrokenSockets((wipeme)); - - // Re-check after the waiting lock has been reacquired - if (m_bClosing) - throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - - sendBackup_RetryWaitBlocked(unstableLinks, (parallel), (final_stat), (none_succeeded), (w_mc), (cx)); - - sendBackup_SilenceRedundantLinks((parallel)); + sendBackup_SilenceRedundantLinks((sendBackupCtx), currtime); // (closing condition checked inside this call) if (none_succeeded) @@ -4132,7 +4003,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // Note that list::size() is linear time, however this shouldn't matter, // as with the increased number of links in the redundancy group the // impossibility of using that many of them grows exponentally. - size_t grpsize = m_Group.size(); + const size_t grpsize = m_Group.size(); if (w_mc.grpdata_size < grpsize) { @@ -4169,9 +4040,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } HLOGC(gslog.Debug, - log << "grp/sendBackup: successfully sent " << final_stat << " bytes, " + log << "grp/sendBackup: successfully sent " << group_send_result << " bytes, " << (ready_again ? "READY for next" : "NOT READY to send next")); - return final_stat; + return group_send_result; } // [[using locked(this->m_GroupLock)]] @@ -4223,6 +4094,72 @@ int32_t CUDTGroup::addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& return m_SenderBuffer.front().mc.pktseq; } +int CUDTGroup::sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq, + size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx) +{ + if (w_mc.srctime == 0) + w_mc.srctime = count_microseconds(currtime.time_since_epoch()); + + SRT_ASSERT(w_nsuccessful == 0); + SRT_ASSERT(w_maxActiveWeight == 0); + + int group_send_result = SRT_ERROR; + + // TODO: implement iterator over active links + typedef vector::const_iterator const_iter_t; + for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) + { + if (!isStateActive(member->state)) + continue; + + SocketData* d = member->pSocketData; + int erc = SRT_SUCCESS; + // Remaining sndstate is SRT_GST_RUNNING. Send a payload through it. + CUDT& u = d->ps->core(); + const int32_t lastseq = u.schedSeqNo(); + int sndresult = SRT_ERROR; + try + { + // This must be wrapped in try-catch because on error it throws an exception. + // Possible return values are only 0, in case when len was passed 0, or a positive + // >0 value that defines the size of the data that it has sent, that is, in case + // of Live mode, equal to 'len'. + sndresult = u.sendmsg2(buf, len, (w_mc)); + } + catch (CUDTException& e) + { + w_cx = e; + erc = e.getErrorCode(); + sndresult = SRT_ERROR; + } + + const bool send_succeeded = sendBackup_CheckSendStatus( + currtime, + sndresult, + lastseq, + w_mc.pktseq, + (u), + (w_curseq), + (group_send_result)); + + if (send_succeeded) + { + ++w_nsuccessful; + w_maxActiveWeight = max(w_maxActiveWeight, d->weight); + } + else if (erc == SRT_EASYNCSND) + { + sendBackup_AssignBackupState(u, BKUPST_ACTIVE_UNSTABLE, currtime); + w_sendBackupCtx.updateMemberState(d, BKUPST_ACTIVE_UNSTABLE); + } + + d->sndresult = sndresult; + d->laststatus = d->ps->getStatus(); + } + + return group_send_result; +} + // [[using locked(this->m_GroupLock)]] int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { @@ -4252,7 +4189,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) size_t skip_initial = 0; if (curseq != core.schedSeqNo()) { - int distance = CSeqNo::seqoff(core.schedSeqNo(), curseq); + const int distance = CSeqNo::seqoff(core.schedSeqNo(), curseq); if (distance < 0) { // This may happen in case when the link to be activated is already running. @@ -4261,8 +4198,8 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // packets that are in the past towards the scheduling sequence. skip_initial = -distance; LOGC(gslog.Warn, - log << "sendBackupRexmit: OVERRIDE attempt to %" << core.schedSeqNo() << " from BACKWARD %" << curseq - << " - DENIED; skip " << skip_initial << " packets"); + log << "sendBackupRexmit: OVERRIDE attempt. Link seqno %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq + << " - DENIED; skip " << skip_initial << " pkts, " << m_SenderBuffer.size() << " pkts in buffer"); } else { @@ -4279,12 +4216,16 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) } } - senderBuffer_t::iterator i = m_SenderBuffer.begin(); if (skip_initial >= m_SenderBuffer.size()) + { + LOGC(gslog.Warn, + log << "sendBackupRexmit: All packets were skipped. Nothing to send %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq + << " - DENIED; skip " << skip_initial << " packets"); return 0; // can't return any other state, nothing was sent - else if (skip_initial) - i += skip_initial; + } + + senderBuffer_t::iterator i = m_SenderBuffer.begin() + skip_initial; // Send everything - including the packet freshly added to the buffer for (; i != m_SenderBuffer.end(); ++i) @@ -4422,7 +4363,8 @@ int CUDTGroup::configure(const char* str) } break;*/ - + case SRT_GTYPE_BROADCAST: + case SRT_GTYPE_BACKUP: default: if (config == "") { diff --git a/srtcore/group.h b/srtcore/group.h index 1477a4583..ad779e39f 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -20,6 +20,7 @@ Written by #include "common.h" #include "packet.h" #include "group_common.h" +#include "group_backup.h" #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; @@ -32,7 +33,9 @@ class CUDTGroup typedef srt::sync::steady_clock::time_point time_point; typedef srt::sync::steady_clock::duration duration; typedef srt::sync::steady_clock steady_clock; - typedef srt::groups::SocketData SocketData; + typedef srt::groups::SocketData SocketData; + typedef srt::groups::SendBackupCtx SendBackupCtx; + typedef srt::groups::BackupMemberState BackupMemberState; public: typedef SRT_MEMBERSTATUS GroupState; @@ -211,102 +214,101 @@ class CUDTGroup int sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc); // Support functions for sendBackup and sendBroadcast + /// Check if group member is idle. + /// @param d group member + /// @param[in,out] w_wipeme array of sockets to remove from group + /// @param[in,out] w_pendingLinks array of sockets pending for connection + /// @returns true if d is idle (standby), false otherwise bool send_CheckIdle(const gli_t d, std::vector& w_wipeme, std::vector& w_pendingLinks); + + + /// This function checks if the member has just become idle (check if sender buffer is empty) to send a KEEPALIVE immidiatelly. + /// @todo Check it is some abandoned logic. void sendBackup_CheckIdleTime(gli_t w_d); /// Qualify states of member links. /// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]] + /// @param[out] w_sendBackupCtx the context will be updated with state qualifications /// @param[in] currtime current timestamp - /// @param[out] w_wipeme broken links or links about to be closed - /// @param[out] w_idleLinks idle links (connected, but not used for transmission) - /// @param[out] w_pendingSockets sockets pending to be connected - /// @param[out] w_unstableLinks active member links qualified as unstable - /// @param[out] w_activeLinks all active member links, including unstable - void sendBackup_QualifyMemberStates(const steady_clock::time_point& currtime, - std::vector& w_wipeme, - std::vector& w_idleLinks, - std::vector& w_pendingSockets, - std::vector& w_unstableLinks, - std::vector& w_activeLinks); - - /// Check if a running link is stable. - /// @retval true running link is stable - /// @retval false running link is unstable - bool sendBackup_CheckRunningStability(const gli_t d, const time_point currtime); + void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + void sendBackup_AssignBackupState(CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); + + /// Qualify the state of the active link: fresh, stable, unstable, wary. + /// @retval active backup member state: fresh, stable, unstable, wary. + BackupMemberState sendBackup_QualifyActiveState(const gli_t d, const time_point currtime); + + BackupMemberState sendBackup_QualifyIfStandBy(const gli_t d); + + /// Sends the same payload over all active members. + /// @param[in] buf payload + /// @param[in] len payload length in bytes + /// @param[in,out] w_mc message control + /// @param[in] currtime current time + /// @param[in] currseq current packet sequence number + /// @param[out] w_nsuccessful number of members with successfull sending. + /// @param[in,out] maxActiveWeight + /// @param[in,out] sendBackupCtx context + /// @param[in,out] w_cx error + /// @return group send result: -1 if sending over all members has failed; number of bytes sent overwise. + int sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq, + size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx); /// Check link sending status - /// @param[in] d Group member iterator /// @param[in] currtime Current time (logging only) - /// @param[in] stat Result of sending over the socket + /// @param[in] send_status Result of sending over the socket /// @param[in] lastseq Last sent sequence number before the current sending operation /// @param[in] pktseq Packet sequence number currently tried to be sent /// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo) /// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links) - /// @param[out] w_parallel Parallel link container (will be filled inside this function) - /// @param[out] w_final_stat Status to be reported by this function eventually - /// @param[out] w_maxActiveWeight Maximum weight value of active links - /// @param[out] w_nsuccessful Updates the number of successful links - /// @param[out] w_is_unstable Set true if sending resulted in AGAIN error. + /// @param[out] w_final_stat w_final_stat = send_status if sending succeded. /// /// @returns true if the sending operation result (submitted in stat) is a success, false otherwise. - bool sendBackup_CheckSendStatus(const gli_t d, - const time_point& currtime, - const int stat, - const int erc, + bool sendBackup_CheckSendStatus(const time_point& currtime, + const int send_status, const int32_t lastseq, const int32_t pktseq, CUDT& w_u, int32_t& w_curseq, - std::vector& w_parallel, - int& w_final_stat, - uint16_t& w_maxActiveWeight, - size_t& w_nsuccessful, - bool& w_is_unstable); + int& w_final_stat); void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc); - /// Check activation conditions and activate a backup link if needed. - /// Backup link activation is needed if: - /// - /// 1. All currently active links are unstable. - /// Note that unstable links still count as sendable; they - /// are simply links that were qualified for sending, but: - /// - have exceeded response timeout - /// - have hit EASYNCSND error during sending - /// - /// 2. Another reason to activate might be if one of idle links - /// has a higher weight than any link currently active - /// (those are collected in 'sendable_pri'). - /// If there are no sendable, a new link needs to be activated anyway. - bool sendBackup_IsActivationNeeded(const std::vector& idleLinks, - const std::vector& unstable, - const std::vector& sendable, - const uint16_t max_sendable_weight, - std::string& activate_reason) const; - - size_t sendBackup_TryActivateIdleLink(const std::vector& idleLinks, - const char* buf, - const int len, - bool& w_none_succeeded, - SRT_MSGCTRL& w_mc, - int32_t& w_curseq, - int32_t& w_final_stat, - CUDTException& w_cx, - std::vector& w_parallel, - std::vector& w_wipeme, - const std::string& activate_reason); - - /// Check if pending sockets are to be closed. - /// @param[in] pending pending sockets - /// @param[in,out] w_wipeme a list of sockets to be removed from the group - void send_CheckPendingSockets(const std::vector& pending, std::vector& w_wipeme); + size_t sendBackup_TryActivateStandbyIfNeeded( + const char* buf, + const int len, + bool& w_none_succeeded, + SRT_MSGCTRL& w_mc, + int32_t& w_curseq, + int32_t& w_final_stat, + SendBackupCtx& w_sendBackupCtx, + CUDTException& w_cx, + const steady_clock::time_point& currtime); + + /// Check if pending sockets are to be qualified as broken. + /// This qualification later results in removing the socket from a group and closing it. + /// @param[in,out] a context with a list of member sockets, some pending might qualified broken + void sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + /// Check if unstable sockets are to be qualified as broken. + /// The main reason for such qualification is if a socket is unstable for too long. + /// This qualification later results in removing the socket from a group and closing it. + /// @param[in,out] a context with a list of member sockets, some pending might qualified broken + void sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); + + /// @brief Marks broken sockets as closed. Used in broadcast sending. + /// @param w_wipeme a list of sockets to close void send_CloseBrokenSockets(std::vector& w_wipeme); - void sendBackup_RetryWaitBlocked(const std::vector& unstable, - std::vector& w_parallel, + + /// @brief Marks broken sockets as closed. Used in backup sending. + /// @param w_sendBackupCtx the context with a list of broken sockets + void sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx); + + void sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx, int& w_final_stat, bool& w_none_succeeded, SRT_MSGCTRL& w_mc, CUDTException& w_cx); - void sendBackup_SilenceRedundantLinks(std::vector& w_parallel); + void sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); void send_CheckValidSockets(); diff --git a/srtcore/group_backup.cpp b/srtcore/group_backup.cpp new file mode 100644 index 000000000..9adb19607 --- /dev/null +++ b/srtcore/group_backup.cpp @@ -0,0 +1,159 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#include "platform_sys.h" +#include +#include + +#include "group_backup.h" + + +namespace srt +{ +namespace groups +{ + +using namespace std; +using namespace srt_logging; + +const char* stateToStr(BackupMemberState state) +{ + switch (state) + { + case srt::groups::BKUPST_UNKNOWN: + return "UNKNOWN"; + case srt::groups::BKUPST_PENDING: + return "PENDING"; + case srt::groups::BKUPST_STANDBY: + return "STANDBY"; + case srt::groups::BKUPST_ACTIVE_FRESH: + return "ACTIVE_FRESH"; + case srt::groups::BKUPST_ACTIVE_STABLE: + return "ACTIVE_STABLE"; + case srt::groups::BKUPST_ACTIVE_UNSTABLE: + return "ACTIVE_UNSTABLE"; + case srt::groups::BKUPST_ACTIVE_UNSTABLE_WARY: + return "ACTIVE_UNSTABLE_WARY"; + case srt::groups::BKUPST_BROKEN: + return "BROKEN"; + default: + break; + } + + return "WRONG_STATE"; +} + +/// @brief Compares group members by their weight (higher weight comes first), then state. +/// Higher weight comes first, same weight: stable, then fresh active. +struct FCompareByWeight +{ + /// @returns true if the first argument is less than (i.e. is ordered before) the second. + bool operator()(const BackupMemberStateEntry& a, const BackupMemberStateEntry& b) + { + if (a.pSocketData != NULL && b.pSocketData != NULL + && (a.pSocketData->weight != b.pSocketData->weight)) + return a.pSocketData->weight > b.pSocketData->weight; + + if (a.state != b.state) + { + SRT_STATIC_ASSERT(BKUPST_ACTIVE_STABLE > BKUPST_ACTIVE_FRESH, "Wrong ordering"); + return a.state > b.state; + } + + // the order does not matter, but comparator must return a different value for not equal a and b + return a.socketID < b.socketID; + } +}; + +void SendBackupCtx::recordMemberState(SocketData* pSockData, BackupMemberState st) +{ + m_memberStates.push_back(BackupMemberStateEntry(pSockData, st)); + ++m_stateCounter[st]; + + if (st == BKUPST_STANDBY) + { + m_standbyMaxWeight = max(m_standbyMaxWeight, pSockData->weight); + } + else if (isStateActive(st)) + { + m_activeMaxWeight = max(m_activeMaxWeight, pSockData->weight); + } +} + +void SendBackupCtx::updateMemberState(const SocketData* pSockData, BackupMemberState st) +{ + typedef vector::iterator iter_t; + for (iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + if (i->pSocketData == NULL) + continue; + + if (i->pSocketData != pSockData) + continue; + + if (i->state == st) + return; + + --m_stateCounter[i->state]; + ++m_stateCounter[st]; + i->state = st; + + return; + } + + + LOGC(gslog.Error, + log << "IPE: SendBackupCtx::updateMemberState failed to locate member"); +} + +void SendBackupCtx::sortByWeightAndState() +{ + sort(m_memberStates.begin(), m_memberStates.end(), FCompareByWeight()); +} + +BackupMemberState SendBackupCtx::getMemberState(const SocketData* pSockData) const +{ + typedef vector::const_iterator const_iter_t; + for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + if (i->pSocketData != pSockData) + continue; + + return i->state; + } + + // The entry was not found + // TODO: Maybe throw an exception here? + return BKUPST_UNKNOWN; +} + +unsigned SendBackupCtx::countMembersByState(BackupMemberState st) const +{ + return m_stateCounter[st]; +} + +std::string SendBackupCtx::printMembers() const +{ + stringstream ss; + typedef vector::const_iterator const_iter_t; + for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i) + { + ss << "@" << i->socketID << " w " << i->pSocketData->weight << " state " << stateToStr(i->state) << ", "; + } + return ss.str(); +} + +} // namespace groups +} // namespace srt diff --git a/srtcore/group_backup.h b/srtcore/group_backup.h new file mode 100644 index 000000000..760ccfbe4 --- /dev/null +++ b/srtcore/group_backup.h @@ -0,0 +1,123 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + /***************************************************************************** + Written by + Haivision Systems Inc. + *****************************************************************************/ + +#ifndef INC_SRT_GROUP_BACKUP_H +#define INC_SRT_GROUP_BACKUP_H + +#include "srt.h" +#include "common.h" +#include "group_common.h" + +#include + +namespace srt +{ +namespace groups +{ + enum BackupMemberState + { + BKUPST_UNKNOWN = -1, + + BKUPST_PENDING = 0, + BKUPST_STANDBY = 1, + BKUPST_BROKEN = 2, + + BKUPST_ACTIVE_UNSTABLE = 3, + BKUPST_ACTIVE_UNSTABLE_WARY = 4, + BKUPST_ACTIVE_FRESH = 5, + BKUPST_ACTIVE_STABLE = 6, + + BKUPST_E_SIZE = 7 + }; + + const char* stateToStr(BackupMemberState state); + + inline bool isStateActive(BackupMemberState state) + { + if (state == BKUPST_ACTIVE_FRESH + || state == BKUPST_ACTIVE_STABLE + || state == BKUPST_ACTIVE_UNSTABLE + || state == BKUPST_ACTIVE_UNSTABLE_WARY) + { + return true; + } + + return false; + } + + struct BackupMemberStateEntry + { + BackupMemberStateEntry(SocketData* psock, BackupMemberState st) + : pSocketData(psock) + , socketID(psock->id) + , state(st) + {} + + SocketData* pSocketData; // accessing pSocketDataIt requires m_GroupLock + SRTSOCKET socketID; // therefore socketID is saved separately (needed to close broken sockets) + BackupMemberState state; + }; + + /// @brief A context needed for main/backup sending function. + /// @todo Using gli_t here does not allow to safely store the context outside of the sendBackup calls. + class SendBackupCtx + { + public: + SendBackupCtx() + : m_stateCounter() // default init with zeros + , m_activeMaxWeight() + , m_standbyMaxWeight() + { + } + + /// @brief Adds or updates a record of the member socket state. + /// @param pSocketDataIt Iterator to a socket + /// @param st State of the memmber socket + /// @todo Implement updating member state + void recordMemberState(SocketData* pSocketDataIt, BackupMemberState st); + + /// @brief Updates a record of the member socket state. + /// @param pSocketDataIt Iterator to a socket + /// @param st State of the memmber socket + /// @todo To be replaced by recordMemberState + /// @todo Update max weights? + void updateMemberState(const SocketData* pSocketDataIt, BackupMemberState st); + + /// @brief sorts members in order + /// Higher weight comes first, same weight: stable first, then fresh active. + void sortByWeightAndState(); + + BackupMemberState getMemberState(const SocketData* pSocketDataIt) const; + + unsigned countMembersByState(BackupMemberState st) const; + + const std::vector& memberStates() const { return m_memberStates; } + + uint16_t maxStandbyWeight() const { return m_standbyMaxWeight; } + uint16_t maxActiveWeight() const { return m_activeMaxWeight; } + + std::string printMembers() const; + + private: + std::vector m_memberStates; // TODO: consider std::map here? + unsigned m_stateCounter[BKUPST_E_SIZE]; + uint16_t m_activeMaxWeight; + uint16_t m_standbyMaxWeight; + }; + +} // namespace groups +} // namespace srt + +#endif // INC_SRT_GROUP_BACKUP_H diff --git a/srtcore/group_common.cpp b/srtcore/group_common.cpp index 8716e343a..536bdf52c 100644 --- a/srtcore/group_common.cpp +++ b/srtcore/group_common.cpp @@ -53,7 +53,8 @@ SocketData prepareSocketData(CUDTSocket* s) false, false, false, - 0 // weight + 0, // weight + 0 // pktSndDropTotal }; return sd; } diff --git a/srtcore/group_common.h b/srtcore/group_common.h index f6ac123c6..d780d0b9a 100644 --- a/srtcore/group_common.h +++ b/srtcore/group_common.h @@ -26,32 +26,35 @@ namespace srt { namespace groups { -typedef SRT_MEMBERSTATUS GroupState; - -struct SocketData -{ - SRTSOCKET id; // same as ps->m_SocketID - CUDTSocket* ps; - int token; - SRT_SOCKSTATUS laststatus; - GroupState sndstate; - GroupState rcvstate; - int sndresult; - int rcvresult; - sockaddr_any agent; - sockaddr_any peer; - bool ready_read; - bool ready_write; - bool ready_error; - - // Configuration - uint16_t weight; -}; - -SocketData prepareSocketData(CUDTSocket* s); - -typedef std::list group_t; -typedef group_t::iterator gli_t; + typedef SRT_MEMBERSTATUS GroupState; + + struct SocketData + { + SRTSOCKET id; // same as ps->m_SocketID + CUDTSocket* ps; + int token; + SRT_SOCKSTATUS laststatus; + GroupState sndstate; + GroupState rcvstate; + int sndresult; + int rcvresult; + sockaddr_any agent; + sockaddr_any peer; + bool ready_read; + bool ready_write; + bool ready_error; + + // Configuration + uint16_t weight; + + // Stats + int64_t pktSndDropTotal; + }; + + SocketData prepareSocketData(CUDTSocket* s); + + typedef std::list group_t; + typedef group_t::iterator gli_t; } // namespace groups } // namespace srt From 14225721a1e16710701bb6d2ce89b9f962172509 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 10 May 2021 15:09:49 +0200 Subject: [PATCH 064/683] [docs] Added Main/Backup mode overview (#1989) --- docs/README.md | 34 ++-- docs/features/bonding-main-backup.md | 248 +++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 docs/features/bonding-main-backup.md diff --git a/docs/README.md b/docs/README.md index 2d91117d7..3e5ce72c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,5 @@ # Documentation Overview - - ## SRT API Documents | Document Title | Folder | File Name | Description | @@ -40,28 +33,37 @@ | Document Title | Folder | File Name | Description | | :----------------------------------------------------------- | :---------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | -| [SRT Access Control](features/access-control.md)
[(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | +| [SRT Access Control
(Stream ID) Guidelines](features/access-control.md) | [features](features/) | [access-control.md](features/access-control.md) | Access Control (Stream ID) guidelines. | | [SRT Connection Bonding](features/bonding-intro.md) | [features](features/) | [bonding-intro.md](features/bonding-intro.md) | Introduction to Connection Bonding. Description
of group (bonded) connections. | -| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | -| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1) and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00) additionally. | -| [Live Streaming](features/live-streaming.md)
[Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | -| [SRT Packet](features/packet-filtering-and-fec.md)
[Filtering & FEC](features/packet-filtering-and-fec.md) | [features](features/) | [packet-filtering-and-fec.md](features/packet-filtering-and-fec.md) | Description of SRT packet filtering mechanism,
including FEC. | | [SRT Socket Groups](features/socket-groups.md) | [features](features/) | [socket-groups.md](features/socket-groups.md) | Description of socket groups in SRT (Connection
Bonding). Here you will also find the information
regarding the `srt-test-live` application for testing
Connection Bonding. | +| [SRT Connection Bonding: Main/Backup][main-backup] | [features](features/) | [bonding-main-backup.md][main-backup] | SRT Main/Backup Connection Bonding. | +| [SRT Encryption](features/encryption.md) | [features](features/) | [encryption.md](features/encryption.md) | Description of SRT encryption mechanism. This
document might be outdated, please consult
[Section 6. Encryption][srt-rfc-sec-6] of the [SRT RFC][srt-rfc] additionally. | +| [SRT Handshake](features/handshake.md) | [features](features/) | [handshake.md](features/handshake.md) | Description of SRT handshake mechanism. This
document might be outdated, please consult
[Section 3.2.1 Handshake][srt-rfc-sec-3-2-1] and
[Section 4.3 Handshake Messages](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-4.3) of the
[SRT RFC][srt-rfc] additionally. | +| [Live Streaming
Guidelines](features/live-streaming.md) | [features](features/) | [live-streaming.md](features/live-streaming.md) | Guidelines for live streaming with SRT. See also
best practices and configuration tips in
[Section 7.1 Live Streaming](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-7.1) of the [SRT RFC][srt-rfc]. | +| [SRT Packet
Filtering & FEC][packet-filter] | [features](features/) | [packet-filtering-and-fec.md][packet-filter] | Description of SRT packet filtering mechanism,
including FEC. | | | | | | ## Sample Applications | Document Title | Folder | File Name | Description | | :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | -| [Using the](apps/srt-live-transmit.md)
[`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | -| [Using the](apps/srt-multiplex.md)
[`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | -| [Using the](apps/srt-tunnel.md)
[`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the
`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | +| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | | | | | | ## Miscellaneous | Document Title | Folder | File Name | Description | | :------------------------------------------------- | :---------------------------- | :---------------------------------------------------- | :----------------------------------------------------------- | -| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1) of the [SRT RFC](https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00). | +| [Why SRT Was Created](misc/why-srt-was-created.md) | [misc](misc/) | [why-srt-was-created.md](misc/why-srt-was-created.md) | Background and history of SRT. See also
[Section 1. Introduction][srt-rfc-sec-1] of the [SRT RFC][srt-rfc]. | | | | | | + +[srt-rfc]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00 +[srt-rfc-sec-1]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-1 +[srt-rfc-sec-3-2-1]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-3.2.1 +[srt-rfc-sec-6]: https://datatracker.ietf.org/doc/html/draft-sharabayko-srt-00#section-6 + +[main-backup]: features/bonding-main-backup.md +[packet-filter]: features/packet-filtering-and-fec.md diff --git a/docs/features/bonding-main-backup.md b/docs/features/bonding-main-backup.md new file mode 100644 index 000000000..59fe78834 --- /dev/null +++ b/docs/features/bonding-main-backup.md @@ -0,0 +1,248 @@ +# SRT Connection Bonding: Main/Backup + +I. [Introduction](#i-introduction) +II. [Mode Overview](#ii-mode-overview) +III. [Sender Logic](#iii-sender-logic) +IV. [Sending Algorithm](#iV-sending-algorithm) + +## I. Introduction + +*SRT Main/Backup Switching* saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. + +This feature is useful for broadcasting where the traffic costs for a main link are relatively low, and the main link is reasonably reliable. To make sure a transmission doesn’t fail, one or several backup connections are on stand-by. These backup connections ensure reliability, but the traffic costs may be high. To save costs, the backup links are only activated when the main link fails or doesn’t achieve the required throughput. + +The goal of SRT's main/backup bonding mode is to identify a potential link break before it is finally confirmed, thus providing a time window to seamlessly switch to one of the backup links. + +## II. Mode Overview + +### Mode Constraints + +**Main constraints** of the main/backup mode are: + +- **only one active link at a time**, except for the period of switching to one of the backup links or to a higher weight link; +- **seamless switching** to one of the backup links ideally without packet drops, but a certain period of packet drop is acceptable during the switch if reasonable (e.g. severe conditions). + +Transmission happens over the main link until it is considered broken or is presumably about to become broken. + +### Sensitivity Modes + +The only sensitivity mode implemented at the moment is a **precautions switch** from an unstable main path to a backup path before an unstable path becomes broken. The goal is to predict an upcoming link breakage before it finally happens, and be ready to switch to an activated backup link loosing as minimum packet as possible. + +Potential improvement for the future may be an additional sensitivity mode: **handover switch**. For the cases where low latency and packet loss is not that critical, the switch can take place once the link is actually broken, without trying to predict it. As the main path is already broken, activating the backup path happens late and there will be a discontinuity in streaming (data loss during the switch). + +### Mode Limitations + +Detecting possible link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY`, there are good chances to not lose a single packet. Therefore, one of the limitations (or usage guidelines) is `SRTO_PEERLATENCY ≥ 3×RTT`. + +The logic of the main/backup sending algorithm is triggered each time an application submits new payload (calls `srt_sendmsg2(..)`). This imposes two limitations: + +- new packets are better be submitted no later than every 50 or 60 ms to trigger the logic with enough frequency (bitrate should be > 180 kbps); +- file transmission logic does not fit well, and not supported anyway. + +## III. Sender Logic + +### Member Link State + +In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. + +Member link status can be: + +1. **SRT_GST_PENDING**. The link is visible, but the connection hasn't been established yet. The link cannot be used for data transmission yet. +2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per 1 second. +3. **SRT_GST_RUNNING** (**active**). The link is being used for payload transmission. + 1. **Fresh-Activated**. A link was freshly (newly) activated, and it is too early to qualify it as either stable or unstable. + 2. **Stable**. Active link is considered stable. + 3. **Unstable**. Active link is considered unstable, e.g. response time exceeded some threshold. + 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold), now a response, which makes it potentially stable again. +4. **SRT_GST_BROKEN**. The link has just been detected broken. It is about to be closed and removed from the group. + +### Member State Transition + +The initial state for group sender logic to operate on a member is once a member socket is connected and transitions to **SRT_GST_IDLE**. + +| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (unstable) | q4 (wary) | q5 =>(broken) | +| ---------------------------------------------------------------- | ---------------------------------- | -------------------------------- | -------------------------------- | ------------------------------ | ---------- | ------------- | +| Last response < LST* | x | q1 | q2 | **q4** (`m_tsWarySince` = now) | x | x | +| Last response >= LST* | x | **q3** (`tsUnstableSince` = now) | **q3** (`tsUnstableSince` = now) | x | **q3** | x | +| `tsUnstableSince`!= 0 and (now - `tsUnstableSince`> PEERIDLETMO) | x | x | x | **q5** | **q5** | x | +| Probing period is over | x | q2 / `tsFreshActivation` = 0 | x | x | **q2** | x | +| Activate (group decision) | **q1** (`tsFreshActivation` = now) | x | x | x | x | x | +| Silence (group decision) | x | :question: | **q0** | x | :question: | x | +| Close/break (external event) | **q5** | **q5** | **q5** | **q5** | **q5** | x | + +Member activation happens according to conditions described in the [Member Activation](#member-activation) section. In case of activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. + +A member in the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state can either become **q2** (**SRT_GST_RUNNING**: **Stable**) or **q3** (**SRT_GST_RUNNING**: **Unstable**). + +Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. A member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. + +An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) can **either**: + +- transition back to the **q2** (**SRT_GST_RUNNING**: **Stable**) state through the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state; +- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); +- transition to the **q5** (**SRT_GST_BROKEN**) state (be closed and removed from the group). + +A member in the **q5** (**SRT_GST_BROKEN**) state will eventually be closed and removed from the group. + +### Member Ordering by Priority + +Comparing two members, the one is ordered before, for which one of the following ordering conditions wins (in the order of priority). + +1. Higher weight (highest priority) +2. By backup state (if equal weight): + 1. **SRT_GST_RUNNING**: **Stable** + 2. **SRT_GST_RUNNING**: **Fresh-Activated** + 3. **SRT_GST_RUNNING**: **Unstable-Wary** + 4. **SRT_GST_RUNNING**: **Unstable** + 5. **SRT_GST_BROKEN** + 6. **SRT_GST_IDLE** (**stand by**) + 7. **SRT_GST_PENDING** +3. By the Socket ID (lower value first). + +For example, an unstable member with a higher weight is ordered before a stable member with lower weight. + +**Potential Improvement**: Order by connection start time (older connection comes first) before comparing socket IDs. + +### Member Activation + +Member activation means transitioning an idle (stand by) member with status **SRT_GST_IDLE** into a **SRT_GST_RUNNING**: **Fresh-Activated** state. Member activation time is saved as `tsFreshActivation = CurrentTime`. + +Activation is needed if one of the following is true: + +1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. +2. The weight of one of idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. + +An idle link to be activated is taken from the top of the list of idle links, sorted according to the [Member Ordering by Priority](#send-member-ordering). + +A member remains in the **SRT_GST_RUNNING**: **Fresh-Activated** state while `CurrentTime - tsFreshActivation > ProbingPeriod`, i.e. for the whole probing period: + + `ProbingPeriod = ILST + 50ms` + +Here the **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, + +- `LSTmin = 60ms` ; +- `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket. + +### Qualifying a Member as Unstable + +A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) by any of the reasons described below. + +#### Unstable by Response Timeout + +A member link is considered unstable if the time elapsed since the last response from the peer (`LastRspTime`) exceeds link stability timeout: + +`CurrentTime - LastRspTime > LST` + +where + +- `CurrentTime` is the current time when the next data packet **is submitted to a group** for sending; +- `LastRspTime` is the time of receiving the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) from the SRT receiver by the SRT sender; for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ≥ tsFreshActivation` ; +- `LST` (Link Stability Timeout) is a dynamic value of stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. + +Link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). + +For a member in **SRT_GST_RUNNING**: **Fresh-Activated** state `LST = ILST` (see [Member Activation](#member-activation)). + +For active (**SRT_GST_RUNNING**) members in states **different** from **SRT_GST_RUNNING**: **Fresh-Activated** `LST` is calculated as follows: + +`LST = 2 * SRTT + 4 * RTTVar,` and `LSTmin ≤ LST ≤ SRTO_PEERLATENCY`, + +where + +- `LSTmin = 60ms` ; +- `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket; +- `SRTT` and `RTTVar` are smoothed RTT and RTT variance calculated on an individual socket member in runtime as described is SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. + +#### Unstable by Sending Failure + +If sending a packet (`srt_sendmsg2(..)`) over a member SRT socket has failed with error `SRT_EASYNCSND`, the member is qualified as **q3** (**SRT_GST_RUNNING**: **Unstable**). + +This error indicates that there was not enough free space in sender's buffer to take this data packet for sending. It should not happen under normal conditions and if buffers were configured correctly this kind of error indicates some possible congestion on a path. + +#### Unstable by Sender-Side Packet Drops + +If a member has dropped (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) a packet **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. + +#### Unstable by Receiver-Side Packet Drops (TBD) + +**IMPORTANT: For the time being, the main backup algorithm does not react on lost packets or packets dropped by the receiver.** Note that SRT sender does not know the drop rate on the receiver's side. Receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packet with a total number of packets dropped by the receiver. + +### Qualifying a Member as Broken + +#### Broken by Peer Idle Timeout + +Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket is to be closed by the SRT library. It is also removed from a group. + +#### Broken by Remaining Unstable for Too Long + +The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: + +`CurrentTime - tsUnstableSince > SRTO_PEERIDLETIMEO`. + +### Qualifying a Member as Stable + +Only a member SRT socket in the active (**SRT_GST_RUNNING**) state can be qualified as stable. + +#### Fresh-Activated Becomes Stable + +A fresh activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualifies as unstable). The probing period is defined in the [Member Activation](#member-activation) section. + +#### Unstable Becomes Stable + +If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to a stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. + +If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state for `4 × SRTO_PEERLATENCY`, it can transition to the **SRT_GST_RUNNING**: **Stable** state. + +*Note that if a member becomes unstable **q3** (**SRT_GST_RUNNING**: **Unstable**) again, the `tsWarySince` time will be reset on the next transition to the **Unstable-Wary** state.* + +### Silencing an Active Member + +There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. + +In order to select a stable member to remain active the [Member Ordering by Priority](#send-member-ordering) is applied. All active members ordered after the first stable member are silenced. All active members ordered before the first stable member in the list, including the stable member, remain active. + +## IV. Sending Algorithm + +Group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. + +### 1. Qualify Member States + +Before sending a packet, all member links are qualified to the states described above. + +### 2. Sending the Same Packet over Active Links + +The same data packet is sent (duplicated) over all members qualified as active (**SRT_GST_RUNNING**). + +**If sending fails, the link is re-qualified as unstable** as described in the [Unstable by Sending Failure](#unstable-by-sending-failure) section. + +### 3. Save the Packet Being Sent to the Group SND Buffer + +The group has a separate buffer for packets being sent and to yet acknowledged. If a member socket gets broken, those packets can be resent over a fresh-activated backup member. + +### 4. Check if Backup Link Activation is Needed + +See the [Member Activation](#member-activation) section. + +### 5. [IF] Activate Idle Link + +If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket.. If sending fails (see [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. + +### 6. Check Pending and Broken Sockets + +Check if there are pending sockets that failed to connect, and should be removed from the group. + +Check if there are broken sockets to be removed from the group. + +Check if there are member socket unstable for to long and to be requested to become broken. + +### 7. Wait for Sending Success + +There may be a situation when none of sending succeeded, but there are active members. + +In case of a non-blocking operation mode of the group, the group returns `SRT_EASYNCSND` error. + +In case of a blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned then), or at least one member successfully takes a data packet for sending. + +### 8. Silence Redundant Members + +The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied on this step. From de68ae9d31405799ad7c44c01775e2d4c9c981b3 Mon Sep 17 00:00:00 2001 From: stevomatthews Date: Tue, 11 May 2021 10:45:01 +0200 Subject: [PATCH 065/683] [docs] Corrections to the main backup doc --- docs/features/bonding-main-backup.md | 122 +++++++++++++-------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/docs/features/bonding-main-backup.md b/docs/features/bonding-main-backup.md index 59fe78834..76a6e27d6 100644 --- a/docs/features/bonding-main-backup.md +++ b/docs/features/bonding-main-backup.md @@ -7,60 +7,60 @@ IV. [Sending Algorithm](#iV-sending-algorithm) ## I. Introduction -*SRT Main/Backup Switching* saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. +*SRT Main/Backup Switching* mode saves contribution bandwidth by using only one (main) link at a time, while keeping a stream alive if the main link gets broken. This feature is useful for broadcasting where the traffic costs for a main link are relatively low, and the main link is reasonably reliable. To make sure a transmission doesn’t fail, one or several backup connections are on stand-by. These backup connections ensure reliability, but the traffic costs may be high. To save costs, the backup links are only activated when the main link fails or doesn’t achieve the required throughput. -The goal of SRT's main/backup bonding mode is to identify a potential link break before it is finally confirmed, thus providing a time window to seamlessly switch to one of the backup links. +The goal of SRT's main/backup bonding mode is to identify a potential link break before it happens, thus providing a time window within which to seamlessly switch to one of the backup links. ## II. Mode Overview ### Mode Constraints -**Main constraints** of the main/backup mode are: +The **main constraints** of the main/backup switching mode are: -- **only one active link at a time**, except for the period of switching to one of the backup links or to a higher weight link; -- **seamless switching** to one of the backup links ideally without packet drops, but a certain period of packet drop is acceptable during the switch if reasonable (e.g. severe conditions). +- **only one active link at a time**, except when switching to one of the backup links or to a link with a higher weight; +- **seamless switching** to one of the backup links ideally happens without packet drops, but dropping packets for a certain period of time is acceptable during the switch under certain circumstances (e.g. severe conditions). Transmission happens over the main link until it is considered broken or is presumably about to become broken. -### Sensitivity Modes +### Sensitivity Levels -The only sensitivity mode implemented at the moment is a **precautions switch** from an unstable main path to a backup path before an unstable path becomes broken. The goal is to predict an upcoming link breakage before it finally happens, and be ready to switch to an activated backup link loosing as minimum packet as possible. +The only sensitivity level implemented at the moment is a **pre-emptive switch** to a backup path from an unstable main path before it breaks. The goal is to predict an upcoming link breakage before it happens, and to be ready to switch to an activated backup link while losing as few packets as possible. -Potential improvement for the future may be an additional sensitivity mode: **handover switch**. For the cases where low latency and packet loss is not that critical, the switch can take place once the link is actually broken, without trying to predict it. As the main path is already broken, activating the backup path happens late and there will be a discontinuity in streaming (data loss during the switch). +An additional sensitivity level (**handover switch**) may be added in the future for cases where low latency and packet loss is not critical. The switch would take place once the link is actually broken, without trying to predict it thereby reducing processing overhead. Since the main path in this case is already broken, there would be a delay associated with activating the backup path resulting in a discontinuity in streaming (data loss during the switch). ### Mode Limitations -Detecting possible link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY`, there are good chances to not lose a single packet. Therefore, one of the limitations (or usage guidelines) is `SRTO_PEERLATENCY ≥ 3×RTT`. +Detecting a potential link break and switching to a backup link requires time. If the activation of the backup link happens within the `SRTO_PEERLATENCY` interval, there is a good chance that not a single packet will be lost. Therefore, one of the limitations (or usage guidelines) is to set `SRTO_PEERLATENCY ≥ 3×RTT`. The logic of the main/backup sending algorithm is triggered each time an application submits new payload (calls `srt_sendmsg2(..)`). This imposes two limitations: -- new packets are better be submitted no later than every 50 or 60 ms to trigger the logic with enough frequency (bitrate should be > 180 kbps); -- file transmission logic does not fit well, and not supported anyway. +- it is better to submit new packets no later than every 50 or 60 ms to trigger the logic with enough frequency (the bitrate should be > 180 kbps); +- file transmission logic does not fit well with this algorithm, and is not supported. ## III. Sender Logic ### Member Link State -In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. +In addition to an individual SRT socket state (e.g. `SRTS_CONNECTED`, `SRTS_BROKEN`, etc.; see [srt_getsockstate(..)](./../API/API-functions.md#srt_getsockstate)), a socket member of a group has a member status (see [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS)). The top level member status [SRT_MEMBERSTATUS](./../API/API-functions.md#SRT_MEMBERSTATUS) is visible from the SRT API. However, `SRT_GST_RUNNING` member status can have sub-statuses used by the group sending algorithm. Member link status can be: 1. **SRT_GST_PENDING**. The link is visible, but the connection hasn't been established yet. The link cannot be used for data transmission yet. -2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per 1 second. +2. **SRT_GST_IDLE** (**stand by**). The link is connected and ready for data transmission, but it is not activated for actual payload transmission. KEEPALIVE control packets are being exchanged once per second. 3. **SRT_GST_RUNNING** (**active**). The link is being used for payload transmission. 1. **Fresh-Activated**. A link was freshly (newly) activated, and it is too early to qualify it as either stable or unstable. 2. **Stable**. Active link is considered stable. - 3. **Unstable**. Active link is considered unstable, e.g. response time exceeded some threshold. - 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold), now a response, which makes it potentially stable again. -4. **SRT_GST_BROKEN**. The link has just been detected broken. It is about to be closed and removed from the group. + 3. **Unstable**. Active link is considered unstable, e.g. response time has exceeded some threshold. + 4. **Unstable-Wary**. A link was identified as unstable (e.g. no response longer than some threshold) until a new response makes it potentially stable again. +4. **SRT_GST_BROKEN**. The link has just been detected to be broken. It is about to be closed and removed from the group. ### Member State Transition -The initial state for group sender logic to operate on a member is once a member socket is connected and transitions to **SRT_GST_IDLE**. +The initial state for group sender logic to operate on a member is once the member socket is connected and transitions to **SRT_GST_IDLE**. -| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (unstable) | q4 (wary) | q5 =>(broken) | +| Event \ State | => q0 (GST_IDLE) | q1 (Fresh-Activated) | q2 (Stable) | q3 (Unstable) | q4 (Wary) | q5 =>(Broken) | | ---------------------------------------------------------------- | ---------------------------------- | -------------------------------- | -------------------------------- | ------------------------------ | ---------- | ------------- | | Last response < LST* | x | q1 | q2 | **q4** (`m_tsWarySince` = now) | x | x | | Last response >= LST* | x | **q3** (`tsUnstableSince` = now) | **q3** (`tsUnstableSince` = now) | x | **q3** | x | @@ -70,23 +70,23 @@ The initial state for group sender logic to operate on a member is once a member | Silence (group decision) | x | :question: | **q0** | x | :question: | x | | Close/break (external event) | **q5** | **q5** | **q5** | **q5** | **q5** | x | -Member activation happens according to conditions described in the [Member Activation](#member-activation) section. In case of activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. +Member activation happens according to conditions described in the [Member Activation](#member-activation) section. Upon activation a member transitions to the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state. A member in the **q1** (**SRT_GST_RUNNING**: **Fresh-Activated**) state can either become **q2** (**SRT_GST_RUNNING**: **Stable**) or **q3** (**SRT_GST_RUNNING**: **Unstable**). -Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. A member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. +Conditions to qualify a member as unstable are described in the [Qualifying Member Unstable](#qualifying-member-unstable) section. If the conditions are met, the member is transitioned into the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. -An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) can **either**: +An **Unstable** member (**q3** (**SRT_GST_RUNNING**: **Unstable**) or **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**)) **either**: - transition back to the **q2** (**SRT_GST_RUNNING**: **Stable**) state through the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state; -- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); +- transition to the **q0** (**SRT_GST_IDLE**) state (be silenced); or - transition to the **q5** (**SRT_GST_BROKEN**) state (be closed and removed from the group). A member in the **q5** (**SRT_GST_BROKEN**) state will eventually be closed and removed from the group. ### Member Ordering by Priority -Comparing two members, the one is ordered before, for which one of the following ordering conditions wins (in the order of priority). +When comparing two members, one is ordered before the other depending on which of the following ordering conditions applies (in the order of priority). 1. Higher weight (highest priority) 2. By backup state (if equal weight): @@ -105,41 +105,41 @@ For example, an unstable member with a higher weight is ordered before a stable ### Member Activation -Member activation means transitioning an idle (stand by) member with status **SRT_GST_IDLE** into a **SRT_GST_RUNNING**: **Fresh-Activated** state. Member activation time is saved as `tsFreshActivation = CurrentTime`. +*Member activation* means transitioning an idle (stand by) member with status **SRT_GST_IDLE** to a **SRT_GST_RUNNING**: **Fresh-Activated** state. The time it takes to activate a member is saved as `tsFreshActivation = CurrentTime`. Activation is needed if one of the following is true: -1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. -2. The weight of one of idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. +1. There are no **SRT_GST_RUNNING**: **Stable** or **SRT_GST_RUNNING**: **Fresh-Activated** members. +2. The weight of one of the idle members is higher than the maximum weight of **SRT_GST_RUNNING** links. -An idle link to be activated is taken from the top of the list of idle links, sorted according to the [Member Ordering by Priority](#send-member-ordering). +An idle link to be activated is taken from the top of the list of idle links, sorted according to [member ordering priority](#send-member-ordering). A member remains in the **SRT_GST_RUNNING**: **Fresh-Activated** state while `CurrentTime - tsFreshActivation > ProbingPeriod`, i.e. for the whole probing period: `ProbingPeriod = ILST + 50ms` -Here the **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, +Here **Initial Link Stability Timeout** `ILST = max(LSTmin; SRTO_PEERLATENCY)`, - `LSTmin = 60ms` ; - `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket. ### Qualifying a Member as Unstable -A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) by any of the reasons described below. +A member in the active (**SRT_GST_RUNNING**) state can be transitioned to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state (become unstable) for any of the reasons described below. -#### Unstable by Response Timeout +#### Unstable due to response timeout -A member link is considered unstable if the time elapsed since the last response from the peer (`LastRspTime`) exceeds link stability timeout: +A member link is considered unstable if the time elapsed since the last response from a peer (`LastRspTime`) exceeds the link stability timeout: `CurrentTime - LastRspTime > LST` where -- `CurrentTime` is the current time when the next data packet **is submitted to a group** for sending; -- `LastRspTime` is the time of receiving the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) from the SRT receiver by the SRT sender; for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ≥ tsFreshActivation` ; -- `LST` (Link Stability Timeout) is a dynamic value of stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. +- `CurrentTime` is the time when the next data packet **is submitted to a group** for sending; +- `LastRspTime` is the time when the latest response (*ACK, loss report (NAK), periodic NAK report, KEEP_ALIVE message, or DATA packet in case of bidirectional transmission*) was received from the SRT receiver by the SRT sender for a member in the **SRT_GST_RUNNING**: **Fresh-Activated** state `LastRspTime ≥ tsFreshActivation`; +- `LST` (Link Stability Timeout) is a dynamic value for stability timeout calculated based on the group `SRT Latency` and RTT estimate on a link. This value is calculated individually for each active (**SRT_GST_RUNNING**) link. -Link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). +The link stability timeout for an active (**SRT_GST_RUNNING**) member (**except** for **SRT_GST_RUNNING**: **Fresh-Activated**) is calculated with each data packet submission (on `srt_sendmsg2(..)`). For a member in **SRT_GST_RUNNING**: **Fresh-Activated** state `LST = ILST` (see [Member Activation](#member-activation)). @@ -149,33 +149,33 @@ For active (**SRT_GST_RUNNING**) members in states **different** from **SRT_GST where -- `LSTmin = 60ms` ; +- `LSTmin = 60ms`; - `SRTO_PEERLATENCY` is the corresponding socket option value on a connected socket; -- `SRTT` and `RTTVar` are smoothed RTT and RTT variance calculated on an individual socket member in runtime as described is SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. +- `SRTT` and `RTTVar` are smoothed RTT and RTT variances calculated on an individual socket member in runtime as described in the SRT RFT [Round Trip Time Estimation](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.10) section. -#### Unstable by Sending Failure +#### Unstable due to sending failure If sending a packet (`srt_sendmsg2(..)`) over a member SRT socket has failed with error `SRT_EASYNCSND`, the member is qualified as **q3** (**SRT_GST_RUNNING**: **Unstable**). -This error indicates that there was not enough free space in sender's buffer to take this data packet for sending. It should not happen under normal conditions and if buffers were configured correctly this kind of error indicates some possible congestion on a path. +This error indicates that there was not enough free space in the sender's buffer to accept this data packet for sending. It should not happen under normal conditions and if buffers are configured correctly this kind of error indicates some possible congestion on a path. -#### Unstable by Sender-Side Packet Drops +#### Unstable due to sender-side packet drops -If a member has dropped (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) a packet **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. +If a member has dropped a packet (see SRT RFC [Too-Late Packet Drop](https://tools.ietf.org/html/draft-sharabayko-srt-00#section-4.6) section) **since the previous submission of a data packet** for sending (previous call to `srt_sendmsg2(..)`), it transitions to the **q3** (**SRT_GST_RUNNING**: **Unstable**) state. -#### Unstable by Receiver-Side Packet Drops (TBD) +#### Unstable due to receiver-side packet drops (TBD) -**IMPORTANT: For the time being, the main backup algorithm does not react on lost packets or packets dropped by the receiver.** Note that SRT sender does not know the drop rate on the receiver's side. Receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packet with a total number of packets dropped by the receiver. +**IMPORTANT: For the time being, the main backup algorithm does not react to lost packets or packets dropped by the receiver.** Note that an SRT sender does not know the drop rate on the receiver's side. A receiver acknowledges packets it drops. PR [#1889](https://github.com/Haivision/srt/pull/1889) extends ACK packets to include the total number of packets dropped by the receiver. ### Qualifying a Member as Broken -#### Broken by Peer Idle Timeout +#### Broken due to peer idle timeout -Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket is to be closed by the SRT library. It is also removed from a group. +Similar to a single SRT connection, a member SRT socket is considered broken if there has been no response from a peer for a certain time, defined by the [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option (5 seconds by default). A broken socket will be closed by the SRT library. It is also removed from a group. -#### Broken by Remaining Unstable for Too Long +#### Broken due to remaining unstable for too long -The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: +The only additional condition to break an SRT connection from a group is if a member remains **unstable** for too long. A group can request a socket member to break its connection if the time elapsed since the socket has become unstable (`tsUnstableSince`) exceeds the timeout defined by the [SRTO_PEERIDLETIMEO](./../APISocketOptions.md#SRTO_PEERIDLETIMEO) socket option: `CurrentTime - tsUnstableSince > SRTO_PEERIDLETIMEO`. @@ -183,13 +183,13 @@ The only additional condition to break an SRT connection from a group is if a me Only a member SRT socket in the active (**SRT_GST_RUNNING**) state can be qualified as stable. -#### Fresh-Activated Becomes Stable +#### Freshly-activated becomes stable -A fresh activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualifies as unstable). The probing period is defined in the [Member Activation](#member-activation) section. +A freshly activated member SRT socket (**SRT_GST_RUNNING**: **Fresh-Activated**) is qualified as stable (**SRT_GST_RUNNING**: **Stable**) once the probing period `ProbingPeriod` is over (unless it has already been qualified as unstable). The probing period is defined in the [Member Activation](#member-activation) section. -#### Unstable Becomes Stable +#### Unstable becomes stable -If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to a stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. +If there is no longer a reason to qualify a member as unstable (see [Qualifying a Member as Unstable](#qualifying-a-member-as-unstable)) it can transition to the stable state. A member in the **q3** (**SRT_GST_RUNNING**: **Unstable**) state transitions immediately to the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state. The time of the transition event is saved as `tsWarySince = CurrentTime`. If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state for `4 × SRTO_PEERLATENCY`, it can transition to the **SRT_GST_RUNNING**: **Stable** state. @@ -197,17 +197,17 @@ If a member remains in the **q4** (**SRT_GST_RUNNING**: **Unstable-Wary**) state ### Silencing an Active Member -There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. +There must be only one stable (**SRT_GST_RUNNING**: **Stable**) member SRT socket active in a group. There may be several active unstable or fresh activated sockets in a group. However, if more than one member is qualified as stable, only one must remain active. In order to select a stable member to remain active the [Member Ordering by Priority](#send-member-ordering) is applied. All active members ordered after the first stable member are silenced. All active members ordered before the first stable member in the list, including the stable member, remain active. ## IV. Sending Algorithm -Group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. +The group sending workflow is triggered by a submission of the following data packet via the `srt_sendmsg(..)` SRT API function. The following steps apply in order. ### 1. Qualify Member States -Before sending a packet, all member links are qualified to the states described above. +Before sending a packet, all member links are qualified according to the states described above. ### 2. Sending the Same Packet over Active Links @@ -217,7 +217,7 @@ The same data packet is sent (duplicated) over all members qualified as active ( ### 3. Save the Packet Being Sent to the Group SND Buffer -The group has a separate buffer for packets being sent and to yet acknowledged. If a member socket gets broken, those packets can be resent over a fresh-activated backup member. +The group has a separate buffer for packets being sent and to be acknowledged. If a member socket gets broken, those packets can be resent over a freshly-activated backup member. ### 4. Check if Backup Link Activation is Needed @@ -225,7 +225,7 @@ See the [Member Activation](#member-activation) section. ### 5. [IF] Activate Idle Link -If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket.. If sending fails (see [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. +If a member is activated, all buffered packets (see step 3) are submitted to this SRT member socket. If sending fails (see the [Unstable by Sending Failure](#unstable-by-sending-failure) section), another member is activated by following the logic described in the [Member Activation](#member-activation) section. ### 6. Check Pending and Broken Sockets @@ -233,16 +233,16 @@ Check if there are pending sockets that failed to connect, and should be removed Check if there are broken sockets to be removed from the group. -Check if there are member socket unstable for to long and to be requested to become broken. +Check if there are member socket unstable for too long that should be requested to transition to the `broken` state. ### 7. Wait for Sending Success -There may be a situation when none of sending succeeded, but there are active members. +There may be a situation where no sending has succeeded, but there are active members. -In case of a non-blocking operation mode of the group, the group returns `SRT_EASYNCSND` error. +If the group is in non-blocking operation mode, the group returns `SRT_EASYNCSND` error. -In case of a blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned then), or at least one member successfully takes a data packet for sending. +If the group is in blocking operation mode, sending over active members is retried until the sending timeout [`SRTO_SNDTIMEO`](./../API/API-socket-options.md#SRTO_SNDTIMEO) is reached (`SRT_EASYNCSND` error is returned), or at least one member successfully accepts a data packet for sending. ### 8. Silence Redundant Members -The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied on this step. +The rules described in the [Silencing an Active Member](#silencing-an-active-member) section are applied in this step. From 34e14abe3a88863e31ac160b6d321722d89bfd33 Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 11 May 2021 18:45:57 +0800 Subject: [PATCH 066/683] [core] Fix use-after-free in rendezvous queue (#1919) A socket changing its m_bConnecting state from true to false will no longer be removed from the queue when closing. See CUDT::closeInternal(). Therefore it must be removed from the queue in CRendezvousQueue::updateConnStatus(..). --- srtcore/queue.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 61400df22..8fe5809bc 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1009,11 +1009,6 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; ufailed.push_back(fi); - /* - * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. - * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) - * and may crash here on next pass. - */ // i_next was preincremented, but this is guaranteed to point to // the element next to erased one. i_next = m_lRendezvousID.erase(i); @@ -1100,7 +1095,13 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con for (vector::iterator i = ufailed.begin(); i != ufailed.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); + /* + * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. + * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) + * and may crash on next pass. + */ i->u->m_bConnecting = false; + remove(i->u->m_SocketID); // DO NOT close the socket here because in this case it might be // unable to get status from at the right moment. Also only member From 9a09b5374e018468ab1673980fcfce31bcf2ebd3 Mon Sep 17 00:00:00 2001 From: Lewis Kirkaldie Date: Wed, 12 May 2021 09:27:03 +0100 Subject: [PATCH 067/683] [build] Windows Build Script improvements (#1994). Added custom build directory and VCPKG OpenSSL. --- .gitignore | 3 ++ CMakeLists.txt | 7 ++- scripts/build-windows.ps1 | 80 +++++++++++++++++++++++++++----- scripts/set-version-metadata.ps1 | 9 ++++ srtcore/version.h.in | 2 +- 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index e179ad146..10f985468 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ _*/ # Ignode Visual Studio Code temp folder .vs/ .vscode/ + +# Ignore vcpkg submodule +vcpkg/ diff --git a/CMakeLists.txt b/CMakeLists.txt index df43a1e26..98d360671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -790,9 +790,14 @@ MafReadDir(srtcore filelist.maf # Auto generated version file and add it to the HEADERS_srt list. if(DEFINED ENV{APPVEYOR_BUILD_NUMBER}) set(SRT_VERSION_BUILD ON) - set(APPVEYOR_BUILD_NUMBER_STRING $ENV{APPVEYOR_BUILD_NUMBER}) + set(CI_BUILD_NUMBER_STRING $ENV{APPVEYOR_BUILD_NUMBER}) message(STATUS "AppVeyor build environment detected: Adding build number to version header") endif() +if(DEFINED ENV{TEAMCITY_VERSION}) + set(SRT_VERSION_BUILD ON) + set(CI_BUILD_NUMBER_STRING $ENV{CI_BUILD_COUNTER}) + message(STATUS "TeamCity build environment detected: Adding build counter to version header") +endif() configure_file("srtcore/version.h.in" "version.h" @ONLY) diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index 47ccb97f5..ab2d19d2d 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -6,7 +6,7 @@ # # By default produces a VS2019 64-bit Release binary using C++11 threads, without # encryption or unit tests enabled, but including test apps. -# Before enabling any encryption options, please install OpenSSL (or customize) +# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build ################################################################################ param ( @@ -17,7 +17,9 @@ param ( [Parameter()][String]$STATIC_LINK_SSL = "OFF", [Parameter()][String]$CXX11 = "ON", [Parameter()][String]$BUILD_APPS = "ON", - [Parameter()][String]$UNIT_TESTS = "OFF" + [Parameter()][String]$UNIT_TESTS = "OFF", + [Parameter()][String]$BUILD_DIR = "_build", + [Parameter()][String]$VCPKG_OPENSSL = "OFF" ) # cmake can be optionally installed (useful when running interactively on a developer station). @@ -65,7 +67,7 @@ if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERAT if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; } # clear any previous build and create & enter the build directory -$buildDir = Join-Path "$projectRoot" "_build" +$buildDir = Join-Path "$projectRoot" "$BUILD_DIR" Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs" Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null @@ -88,19 +90,20 @@ if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { Start-Process $cmakeMsiFile -Wait Remove-Item $cmakeMsiFile Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script" - exit + throw } else{ Write-Output "Quitting because cmake is required" - exit + throw } } +# get pthreads from nuget if CXX11 is not enabled if ( $CXX11 -eq "OFF" ) { # get pthreads (this is legacy, and is only availble in nuget for VS2015 and VS2013) if ( $VS_VERSION -gt 2015 ) { Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build" - exit + throw } if ( $DEVENV_PLATFORM -eq 'Win32' ) { nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages @@ -110,6 +113,7 @@ if ( $CXX11 -eq "OFF" ) { } } +# check to see if static SSL linking was requested, and enable encryption if not already ON if ( $STATIC_LINK_SSL -eq "ON" ) { if ( $ENABLE_ENCRYPTION -eq "OFF" ) { # requesting a static link implicitly requires encryption support @@ -118,14 +122,63 @@ if ( $STATIC_LINK_SSL -eq "ON" ) { } } +# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON +if ( $VCPKG_OPENSSL -eq "ON" ) { + if ( $ENABLE_ENCRYPTION -eq "OFF" ) { + # requesting VCPKG to provide OpenSSL requires encryption support + Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON" + $ENABLE_ENCRYPTION = "ON" + } +} + # build the cmake command flags from arguments $cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + "-DENABLE_STDCXX_SYNC=$CXX11 " + "-DENABLE_APPS=$BUILD_APPS " + "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + - "-DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " + "-DENABLE_UNITTESTS=$UNIT_TESTS" +# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package +if ( $VCPKG_OPENSSL -eq 'ON' ) { + Push-Location $projectRoot + Write-Output "Cloning VCPKG into: $(Get-Location)" + if (Test-Path -Path ".\vcpkg") { + Set-Location .\vcpkg + git pull + } else { + git clone https://github.com/microsoft/vcpkg + Set-Location .\vcpkg + } + + .\bootstrap-vcpkg.bat + + if($DEVENV_PLATFORM -EQ "x64"){ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x64-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static" + } + else{ + .\vcpkg install openssl:x64-windows + } + } + else{ + if($STATIC_LINK_SSL -EQ "ON"){ + .\vcpkg install openssl:x86-windows-static + $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static" + } + else{ + .\vcpkg install openssl:x86-windows + } + } + + .\vcpkg integrate install + Pop-Location + $cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake" +} +else { + $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " +} + # cmake uses a flag for architecture from vs2019, so add that as a suffix if ( $VS_VERSION -eq '2019' ) { $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" @@ -143,7 +196,8 @@ Invoke-Expression "& $execVar" # check build ran OK, exit if cmake failed if( $LASTEXITCODE -ne 0 ) { - return $LASTEXITCODE + Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" + throw } $ErrorActionPreference = "Stop" @@ -161,13 +215,17 @@ if ( $null -eq $msBuildPath ) { $vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue if ( $null -eq $vsWherePath ) { Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again" - exit + throw } } - $msBuildPath = & $vsWherePath -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 + $msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 + if ( $null -eq $msBuildPath ) { + Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation" + throw + } } -& $msBuildPath SRT.sln /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM +& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM # return to the directory previously occupied before running the script Pop-Location diff --git a/scripts/set-version-metadata.ps1 b/scripts/set-version-metadata.ps1 index cd36cc37d..050838e5e 100644 --- a/scripts/set-version-metadata.ps1 +++ b/scripts/set-version-metadata.ps1 @@ -27,6 +27,15 @@ if($Env:APPVEYOR){ Update-AppveyorBuild -Version "$majorVer.$minorVer.$patchVer.$buildNum" $FileDescriptionBranchCommitValue = "$Env:APPVEYOR_REPO_NAME - $($Env:APPVEYOR_REPO_BRANCH) ($($Env:APPVEYOR_REPO_COMMIT.substring(0,8)))" } +if($Env:TEAMCITY_VERSION){ + #make TeamCity update with this new version number + Write-Output "##teamcity[buildNumber '$majorVer.$minorVer.$patchVer.$buildNum']" + Write-Output "##teamcity[setParameter name='MajorVersion' value='$majorVer']" + Write-Output "##teamcity[setParameter name='MinorVersion' value='$minorVer']" + Write-Output "##teamcity[setParameter name='PatchVersion' value='$patchVer']" + Write-Output "##teamcity[setParameter name='BuildVersion' value='$buildNum']" + $FileDescriptionBranchCommitValue = "$majorVer.$minorVer.$patchVer.$buildNum - ($($Env:BUILD_VCS_NUMBER.substring(0,8)))" +} #find C++ resource files and update file description with branch / commit details $FileDescriptionStringRegex = '(\bVALUE\s+\"FileDescription\"\s*\,\s*\")([^\"]*\\\")*[^\"]*(\")' diff --git a/srtcore/version.h.in b/srtcore/version.h.in index 72ce89bcb..c32ac1226 100644 --- a/srtcore/version.h.in +++ b/srtcore/version.h.in @@ -24,7 +24,7 @@ written by #define SRT_VERSION_MAJOR @SRT_VERSION_MAJOR@ #define SRT_VERSION_MINOR @SRT_VERSION_MINOR@ #define SRT_VERSION_PATCH @SRT_VERSION_PATCH@ -#cmakedefine SRT_VERSION_BUILD @APPVEYOR_BUILD_NUMBER_STRING@ +#cmakedefine SRT_VERSION_BUILD @CI_BUILD_NUMBER_STRING@ #define SRT_VERSION_STRING "@SRT_VERSION@" #define SRT_VERSION_VALUE \ From 930d3468ae256dcb959d96d3a2a38c06c10a4645 Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Wed, 12 May 2021 11:50:20 +0200 Subject: [PATCH 068/683] [build] Added script to build the Windows installer for SRT libraries (#1995). See the scripts/win-installer/README.md file for details. --- scripts/win-installer/.gitignore | 10 + scripts/win-installer/README.md | 33 +++ scripts/win-installer/build-win-intaller.ps1 | 212 ++++++++++++++++++ scripts/win-installer/install-nsis.ps1 | 122 +++++++++++ scripts/win-installer/install-openssl.ps1 | 119 ++++++++++ scripts/win-installer/libsrt.nsi | 217 +++++++++++++++++++ scripts/win-installer/libsrt.props | 19 ++ 7 files changed, 732 insertions(+) create mode 100644 scripts/win-installer/.gitignore create mode 100644 scripts/win-installer/README.md create mode 100644 scripts/win-installer/build-win-intaller.ps1 create mode 100644 scripts/win-installer/install-nsis.ps1 create mode 100644 scripts/win-installer/install-openssl.ps1 create mode 100644 scripts/win-installer/libsrt.nsi create mode 100644 scripts/win-installer/libsrt.props diff --git a/scripts/win-installer/.gitignore b/scripts/win-installer/.gitignore new file mode 100644 index 000000000..ed44eef33 --- /dev/null +++ b/scripts/win-installer/.gitignore @@ -0,0 +1,10 @@ +tmp +installers +*.exe +*~ +~* +.#* +*.bak +*.autosave +.DS_Store +._* diff --git a/scripts/win-installer/README.md b/scripts/win-installer/README.md new file mode 100644 index 000000000..205e03893 --- /dev/null +++ b/scripts/win-installer/README.md @@ -0,0 +1,33 @@ +## SRT Static Libraries Installer for Windows + +This directory contains scripts to build a binary installer for +libsrt on Windows systems for Visual Studio applications using SRT. + +### Building Windows applications with libsrt + +After installing the libsrt binary, an environment variable named `LIBSRT` is +defined to the installation root (typically `C:\Program Files (x86)\libsrt`). + +In this directory, there is a Visual Studio property file named `libsrt.props`. +Simply reference this property file in your Visual Studio project to use libsrt. + +You can also do that manually by editing the application project file (the XML +file named with a `.vcxproj` extension). Add the following line just before +the end of the file: + +~~~ + +~~~ + +### Building the installer + +The first two steps need to be executed once only. Only the last step needs +to be repeated each time a new version of libsrt is available. + +- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. + This can be done automatically by running the PowerShell script `install-openssl.ps1`. +- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. + This can be done automatically by running the PowerShell script `install-nsis.ps1`. +- Build the libsrt installer by running the PowerShell script `build-win-installer.ps1`. + +The installer is then available in the directory `installers`. diff --git a/scripts/win-installer/build-win-intaller.ps1 b/scripts/win-installer/build-win-intaller.ps1 new file mode 100644 index 000000000..261c457fa --- /dev/null +++ b/scripts/win-installer/build-win-intaller.ps1 @@ -0,0 +1,212 @@ +#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Build the SRT static libraries installer for Windows. + + .PARAMETER Version + + Use the specified string as version number from libsrt. By default, if + the current commit has a tag, use that tag (initial 'v' removed). Otherwise, + the defaut version is a detailed version number (most recent version, number + of commits since then, short commit SHA). + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. + +#> +[CmdletBinding()] +param( + [string]$Version = "", + [switch]$NoPause = $false +) +Write-Output "Building the SRT static libraries installer for Windows" + +# Directory containing this script: +$ScriptDir = $PSScriptRoot + +# The root of the srt repository is two levels up. +$RepoDir = (Split-Path -Parent (Split-Path -Parent $ScriptDir)) + +# Output directory for final installers: +$OutDir = "$ScriptDir\installers" + +# Temporary directory for build operations: +$TmpDir = "$ScriptDir\tmp" + + +#----------------------------------------------------------------------------- +# A function to exit this script with optional error message, using -NoPause +#----------------------------------------------------------------------------- + +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + + +#----------------------------------------------------------------------------- +# Build SRT version strings +#----------------------------------------------------------------------------- + +# By default, let git format a decent version number. +if (-not $Version) { + $Version = (git describe --tags ) -replace '-g','-' +} +$Version = $Version -replace '^v','' + +# Split version string in pieces and make sure to get at least four elements. +$VField = ($Version -split "[-\. ]") + @("0", "0", "0", "0") | Select-String -Pattern '^\d*$' +$VersionInfo = "$($VField[0]).$($VField[1]).$($VField[2]).$($VField[3])" + +Write-Output "SRT version: $Version" +Write-Output "Windows version info: $VersionInfo" + + +#----------------------------------------------------------------------------- +# Initialization phase, verify prerequisites +#----------------------------------------------------------------------------- + +# Locate OpenSSL root from local installation. +$SslRoot = @{ + "x64" = "C:\Program Files\OpenSSL-Win64"; + "Win32" = "C:\Program Files (x86)\OpenSSL-Win32" +} + +# Verify OpenSSL directories. +$Missing = 0 +foreach ($file in @($SslRoot["x64"], $SslRoot["Win32"])) { + if (-not (Test-Path $file)) { + Write-Output "**** Missing $file" + $Missing = $Missing + 1 + } +} +if ($Missing -gt 0) { + Exit-Script "Missing $Missing OpenSSL files, use install-openssl.ps1 to install OpenSSL" +} + +# Locate MSBuild and CMake, regardless of Visual Studio version. +Write-Output "Searching MSBuild ..." +$MSRoots = @("C:\Program Files*\MSBuild", "C:\Program Files*\Microsoft Visual Studio", "C:\Program Files*\CMake*") +$MSBuild = Get-ChildItem -Recurse -Path $MSRoots -Include MSBuild.exe -ErrorAction Ignore | + ForEach-Object { (Get-Command $_).FileVersionInfo } | + Sort-Object -Unique -Property FileVersion | + ForEach-Object { $_.FileName} | + Select-Object -Last 1 +if (-not $MSBuild) { + Exit-Script "MSBuild not found" +} + +Write-Output "Searching CMake ..." +$CMake = Get-ChildItem -Recurse -Path $MSRoots -Include cmake.exe -ErrorAction Ignore | + ForEach-Object { (Get-Command $_).FileVersionInfo } | + Sort-Object -Unique -Property FileVersion | + ForEach-Object { $_.FileName} | + Select-Object -Last 1 +if (-not $CMake) { + Exit-Script "CMake not found, check option 'C++ CMake tools for Windows' in Visual Studio installer" +} + +# Locate NSIS, the Nullsoft Scriptable Installation System. +Write-Output "Searching NSIS ..." +$NSIS = Get-Item "C:\Program Files*\NSIS\makensis.exe" | ForEach-Object { $_.FullName} | Select-Object -Last 1 +if (-not $NSIS) { + Exit-Script "NSIS not found, use install-nsis.ps1 to install NSIS" +} + +Write-Output "MSBuild: $MSBuild" +Write-Output "CMake: $CMake" +Write-Output "NSIS: $NSIS" + +# Create the directories for builds when necessary. +[void](New-Item -Path $TmpDir -ItemType Directory -Force) +[void](New-Item -Path $OutDir -ItemType Directory -Force) + + +#----------------------------------------------------------------------------- +# Configure and build SRT library using CMake on two architectures. +#----------------------------------------------------------------------------- + +foreach ($Platform in @("x64", "Win32")) { + + # Build directory. Cleanup to force a fresh cmake config. + $BuildDir = "$TmpDir\build.$Platform" + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir + [void](New-Item -Path $BuildDir -ItemType Directory -Force) + + # Run CMake. + Write-Output "Configuring build for platform $Platform ..." + $SRoot = $SslRoot[$Platform] + & $CMake -S $RepoDir -B $BuildDir -A $Platform ` + -DENABLE_STDCXX_SYNC=ON ` + -DOPENSSL_ROOT_DIR="$SRoot" ` + -DOPENSSL_LIBRARIES="$SRoot\lib\libssl_static.lib;$SRoot\lib\libcrypto_static.lib" ` + -DOPENSSL_INCLUDE_DIR="$SRoot\include" + + # Patch version string in version.h + Get-Content "$BuildDir\version.h" | + ForEach-Object { + $_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`"" + } | + Out-File "$BuildDir\version.new" -Encoding ascii + Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force + + # Compile SRT. + Write-Output "Building for platform $Platform ..." + foreach ($Conf in @("Release", "Debug")) { + & $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static + } +} + +# Verify the presence of compiled libraries. +Write-Output "Checking compiled libraries ..." +$Missing = 0 +foreach ($Conf in @("Release", "Debug")) { + foreach ($Platform in @("x64", "Win32")) { + $Path = "$TmpDir\build.$Platform\$Conf\srt_static.lib" + if (-not (Test-Path $Path)) { + Write-Output "**** Missing $Path" + $Missing = $Missing + 1 + } + } +} +if ($Missing -gt 0) { + Exit-Script "Missing $Missing files" +} + + +#----------------------------------------------------------------------------- +# Build the binary installer. +#----------------------------------------------------------------------------- + +Write-Output "Building installer ..." +& $NSIS /V2 ` + /DVersion="$Version" ` + /DVersionInfo="$VersionInfo" ` + /DOutDir="$OutDir" ` + /DBuildRoot="$TmpDir" ` + /DRepoDir="$RepoDir" ` + "$ScriptDir\libsrt.nsi" + +Exit-Script diff --git a/scripts/win-installer/install-nsis.ps1 b/scripts/win-installer/install-nsis.ps1 new file mode 100644 index 000000000..14879b521 --- /dev/null +++ b/scripts/win-installer/install-nsis.ps1 @@ -0,0 +1,122 @@ +#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Download, expand and install NSIS, the NullSoft Installer Scripting. + + .PARAMETER ForceDownload + + Force a download even if NSIS is already downloaded. + + .PARAMETER NoInstall + + Do not install the NSIS package. By default, NSIS is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [switch]$ForceDownload = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "NSIS download and installation procedure" +$NSISPage = "https://nsis.sourceforge.io/Download" +$FallbackURL = "http://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download" + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Local file names. +$RootDir = $PSScriptRoot +$TmpDir = "$RootDir\tmp" + +# Create the directory for external products when necessary. +[void] (New-Item -Path $TmpDir -ItemType Directory -Force) + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the HTML page for NSIS downloads. +$status = 0 +$message = "" +$Ref = $null +try { + $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $NSISPage + $status = [int] [Math]::Floor($response.StatusCode / 100) +} +catch { + $message = $_.Exception.Message +} + +if ($status -ne 1 -and $status -ne 2) { + # Error fetch NSIS download page. + if ($message -eq "" -and (Test-Path variable:response)) { + Write-Output "Status code $($response.StatusCode), $($response.StatusDescription)" + } + else { + Write-Output "#### Error accessing ${NSISPage}: $message" + } +} +else { + # Parse HTML page to locate the latest installer. + $Ref = $response.Links.href | Where-Object { $_ -like "*/nsis-*-setup.exe?download" } | Select-Object -First 1 +} + +if (-not $Ref) { + # Could not find a reference to NSIS installer. + $Url = [System.Uri]$FallbackURL +} +else { + # Build the absolute URL's from base URL (the download page) and href links. + $Url = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$NSISPage, $Ref) +} + +$InstallerName = (Split-Path -Leaf $Url.LocalPath) +$InstallerPath = "$TmpDir\$InstallerName" + +# Download installer +if (-not $ForceDownload -and (Test-Path $InstallerPath)) { + Write-Output "$InstallerName already downloaded, use -ForceDownload to download again" +} +else { + Write-Output "Downloading $Url ..." + Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $InstallerPath + if (-not (Test-Path $InstallerPath)) { + Exit-Script "$Url download failed" + } +} + +# Install NSIS +if (-not $NoInstall) { + Write-Output "Installing $InstallerName" + Start-Process -FilePath $InstallerPath -ArgumentList @("/S") -Wait +} + +Exit-Script diff --git a/scripts/win-installer/install-openssl.ps1 b/scripts/win-installer/install-openssl.ps1 new file mode 100644 index 000000000..83b59954f --- /dev/null +++ b/scripts/win-installer/install-openssl.ps1 @@ -0,0 +1,119 @@ +#----------------------------------------------------------------------------- +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021, Thierry Lelegard +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#----------------------------------------------------------------------------- + +<# + .SYNOPSIS + + Download, expand and install OpenSSL for Windows. + + .PARAMETER ForceDownload + + Force a download even if the OpenSSL installers are already downloaded. + + .PARAMETER NoInstall + + Do not install the OpenSSL packages. By default, OpenSSL is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [switch]$ForceDownload = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "OpenSSL download and installation procedure" +$OpenSSLHomePage = "http://slproweb.com/products/Win32OpenSSL.html" + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Local file names. +$RootDir = $PSScriptRoot +$TmpDir = "$RootDir\tmp" + +# Create the directory for external products when necessary. +[void] (New-Item -Path $TmpDir -ItemType Directory -Force) + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the HTML page for OpenSSL downloads. +$status = 0 +$message = "" +try { + $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $OpenSSLHomePage + $status = [int] [Math]::Floor($response.StatusCode / 100) +} +catch { + $message = $_.Exception.Message +} +if ($status -ne 1 -and $status -ne 2) { + if ($message -eq "" -and (Test-Path variable:response)) { + Exit-Script "Status code $($response.StatusCode), $($response.StatusDescription)" + } + else { + Exit-Script "#### Error accessing ${OpenSSLHomePage}: $message" + } +} + +# Parse HTML page to locate the latest MSI files. +$Ref32 = $response.Links.href | Where-Object { $_ -like "*/Win32OpenSSL-*.msi" } | Select-Object -First 1 +$Ref64 = $response.Links.href | Where-Object { $_ -like "*/Win64OpenSSL-*.msi" } | Select-Object -First 1 + +# Build the absolute URL's from base URL (the download page) and href links. +$Url32 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref32) +$Url64 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref64) + +# Download and install one MSI package. +function Download-Install([string]$Url) +{ + $MsiName = (Split-Path -Leaf $Url.toString()) + $MsiPath = "$TmpDir\$MsiName" + + if (-not $ForceDownload -and (Test-Path $MsiPath)) { + Write-Output "$MsiName already downloaded, use -ForceDownload to download again" + } + else { + Write-Output "Downloading $Url ..." + Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $MsiPath + } + + if (-not (Test-Path $MsiPath)) { + Exit-Script "$Url download failed" + } + + if (-not $NoInstall) { + Write-Output "Installing $MsiName" + Start-Process msiexec.exe -ArgumentList @("/i", $MsiPath, "/qn", "/norestart") -Wait + } +} + +# Download and install the two MSI packages. +Download-Install $Url32 +Download-Install $Url64 +Exit-Script diff --git a/scripts/win-installer/libsrt.nsi b/scripts/win-installer/libsrt.nsi new file mode 100644 index 000000000..4628642cc --- /dev/null +++ b/scripts/win-installer/libsrt.nsi @@ -0,0 +1,217 @@ +;----------------------------------------------------------------------------- +; +; SRT - Secure, Reliable, Transport +; Copyright (c) 2021, Thierry Lelegard +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. +; +;----------------------------------------------------------------------------- +; +; NSIS script to build the SRT binary installer for Windows. +; Do not invoke NSIS directly, use PowerShell script build-win-installer.ps1 +; to ensure that all parameters are properly passed. +; +;----------------------------------------------------------------------------- + +Name "SRT" +Caption "SRT Libraries Installer" + +!verbose push +!verbose 0 +!include "MUI2.nsh" +!include "Sections.nsh" +!include "TextFunc.nsh" +!include "FileFunc.nsh" +!include "WinMessages.nsh" +!include "x64.nsh" +!verbose pop + +!define ProductName "libsrt" +!define Build32Dir "${BuildRoot}\build.Win32" +!define Build64Dir "${BuildRoot}\build.x64" +!define SSL32Dir "C:\Program Files (x86)\OpenSSL-Win32" +!define SSL64Dir "C:\Program Files\OpenSSL-Win64" + +; Installer file information. +VIProductVersion ${VersionInfo} +VIAddVersionKey ProductName "${ProductName}" +VIAddVersionKey ProductVersion "${Version}" +VIAddVersionKey Comments "The SRT static libraries for Visual C++ on Windows" +VIAddVersionKey CompanyName "Haivision" +VIAddVersionKey LegalCopyright "Copyright (c) 2021 Haivision Systems Inc." +VIAddVersionKey FileVersion "${VersionInfo}" +VIAddVersionKey FileDescription "SRT Installer" + +; Name of binary installer file. +OutFile "${OutDir}\${ProductName}-${Version}.exe" + +; Generate a Unicode installer (default is ANSI). +Unicode true + +; Registry key for environment variables +!define EnvironmentKey '"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +; Registry entry for product info and uninstallation info. +!define ProductKey "Software\${ProductName}" +!define UninstallKey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ProductName}" + +; Use XP manifest. +XPStyle on + +; Request administrator privileges for Windows Vista and higher. +RequestExecutionLevel admin + +; "Modern User Interface" (MUI) settings. +!define MUI_ABORTWARNING + +; Default installation folder. +InstallDir "$PROGRAMFILES\${ProductName}" + +; Get installation folder from registry if available from a previous installation. +InstallDirRegKey HKLM "${ProductKey}" "InstallDir" + +; Installer pages. +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +; Uninstaller pages. +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +; Languages. +!insertmacro MUI_LANGUAGE "English" + +; Installation initialization. +function .onInit + ; In 64-bit installers, don't use registry redirection. + ${If} ${RunningX64} + SetRegView 64 + ${EndIf} +functionEnd + +; Uninstallation initialization. +function un.onInit + ; In 64-bit installers, don't use registry redirection. + ${If} ${RunningX64} + SetRegView 64 + ${EndIf} +functionEnd + +; Installation section +Section "Install" + + ; Work on "all users" context, not current user. + SetShellVarContext all + + ; Delete obsolete files from previous versions. + Delete "$INSTDIR\LICENSE.pthread.txt" + Delete "$INSTDIR\include\srt\srt4udt.h" + Delete "$INSTDIR\include\srt\udt.h" + Delete "$INSTDIR\lib\Release-x64\pthread.lib" + Delete "$INSTDIR\lib\Release-Win32\pthread.lib" + Delete "$INSTDIR\lib\Debug-x64\srt.pdb" + Delete "$INSTDIR\lib\Debug-x64\pthread.pdb" + Delete "$INSTDIR\lib\Debug-x64\pthread.lib" + Delete "$INSTDIR\lib\Debug-Win32\srt.pdb" + Delete "$INSTDIR\lib\Debug-Win32\pthread.pdb" + Delete "$INSTDIR\lib\Debug-Win32\pthread.lib" + + SetOutPath "$INSTDIR" + File /oname=LICENSE.txt "${RepoDir}\LICENSE" + File "libsrt.props" + + ; Header files. + CreateDirectory "$INSTDIR\include\srt" + SetOutPath "$INSTDIR\include\srt" + File "${RepoDir}\srtcore\logging_api.h" + File "${RepoDir}\srtcore\platform_sys.h" + File "${RepoDir}\srtcore\srt.h" + File "${Build64Dir}\version.h" + + CreateDirectory "$INSTDIR\include\win" + SetOutPath "$INSTDIR\include\win" + File "${RepoDir}\common\win\syslog_defs.h" + + ; Libraries. + CreateDirectory "$INSTDIR\lib" + + CreateDirectory "$INSTDIR\lib\Release-x64" + SetOutPath "$INSTDIR\lib\Release-x64" + File /oname=srt.lib "${Build64Dir}\Release\srt_static.lib" + File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MD.lib" + File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MD.lib" + + CreateDirectory "$INSTDIR\lib\Debug-x64" + SetOutPath "$INSTDIR\lib\Debug-x64" + File /oname=srt.lib "${Build64Dir}\Debug\srt_static.lib" + File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MDd.lib" + File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MDd.lib" + + CreateDirectory "$INSTDIR\lib\Release-Win32" + SetOutPath "$INSTDIR\lib\Release-Win32" + File /oname=srt.lib "${Build32Dir}\Release\srt_static.lib" + File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MD.lib" + File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MD.lib" + + CreateDirectory "$INSTDIR\lib\Debug-Win32" + SetOutPath "$INSTDIR\lib\Debug-Win32" + File /oname=srt.lib "${Build32Dir}\Debug\srt_static.lib" + File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MDd.lib" + File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MDd.lib" + + ; Add an environment variable to installation root. + WriteRegStr HKLM ${EnvironmentKey} "LIBSRT" "$INSTDIR" + + ; Store installation folder in registry. + WriteRegStr HKLM "${ProductKey}" "InstallDir" $INSTDIR + + ; Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + + ; Declare uninstaller in "Add/Remove Software" control panel + WriteRegStr HKLM "${UninstallKey}" "DisplayName" "${ProductName}" + WriteRegStr HKLM "${UninstallKey}" "Publisher" "Haivision" + WriteRegStr HKLM "${UninstallKey}" "URLInfoAbout" "https://github.com/Haivision/srt" + WriteRegStr HKLM "${UninstallKey}" "DisplayVersion" "${Version}" + WriteRegStr HKLM "${UninstallKey}" "DisplayIcon" "$INSTDIR\Uninstall.exe" + WriteRegStr HKLM "${UninstallKey}" "UninstallString" "$INSTDIR\Uninstall.exe" + + ; Get estimated size of installed files + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKLM "${UninstallKey}" "EstimatedSize" "$0" + + ; Notify applications of environment modifications + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +SectionEnd + +; Uninstallation section +Section "Uninstall" + + ; Work on "all users" context, not current user. + SetShellVarContext all + + ; Get installation folder from registry + ReadRegStr $0 HKLM "${ProductKey}" "InstallDir" + + ; Delete product registry entries + DeleteRegKey HKCU "${ProductKey}" + DeleteRegKey HKLM "${ProductKey}" + DeleteRegKey HKLM "${UninstallKey}" + DeleteRegValue HKLM ${EnvironmentKey} "LIBSRT" + + ; Delete product files. + RMDir /r "$0\include" + RMDir /r "$0\lib" + Delete "$0\libsrt.props" + Delete "$0\LICENSE*" + Delete "$0\Uninstall.exe" + RMDir "$0" + + ; Notify applications of environment modifications + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +SectionEnd diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props new file mode 100644 index 000000000..d8de447c1 --- /dev/null +++ b/scripts/win-installer/libsrt.props @@ -0,0 +1,19 @@ + + + + + + + + + + $(LIBSRT)\include;%(AdditionalIncludeDirectories) + + + srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) + $(LIBSRT)\lib\$(Configuration)-$(Platform);%(AdditionalLibraryDirectories) + /ignore:4099 %(AdditionalOptions) + + + + From 36f89952037c3715ed7cbfe7d98d89995ed591cf Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 13:46:00 +0200 Subject: [PATCH 069/683] [core] CRendezvousQueue: added doxygen comments. Renamed m_RIDVectorLock to m_RIDListLock. --- srtcore/queue.cpp | 20 ++++++++++---------- srtcore/queue.h | 47 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8fe5809bc..2de1515dd 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -815,7 +815,7 @@ void CHash::remove(int32_t id) // CRendezvousQueue::CRendezvousQueue() : m_lRendezvousID() - , m_RIDVectorLock() + , m_RIDListLock() { } @@ -827,7 +827,7 @@ CRendezvousQueue::~CRendezvousQueue() void CRendezvousQueue::insert( const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); CRL r; r.m_iID = id; @@ -843,7 +843,7 @@ void CRendezvousQueue::insert( void CRendezvousQueue::remove(const SRTSOCKET &id) { - ScopedLock lkv (m_RIDVectorLock); + ScopedLock lkv (m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { @@ -855,12 +855,12 @@ void CRendezvousQueue::remove(const SRTSOCKET &id) } } -CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) +CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); // TODO: optimize search - for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) + for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) { @@ -915,7 +915,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con #endif { - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); if (m_lRendezvousID.empty()) return; @@ -1004,7 +1004,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con } // The call to completeBrokenConnectionDependencies() cannot happen here - // under the lock of m_RIDVectorLock as it risks a deadlock. Collect it + // under the lock of m_RIDListLock as it risks a deadlock. Collect it // to update later. LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; ufailed.push_back(fi); @@ -1026,7 +1026,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { IF_HEAVY_LOGGING(++debug_nupd); - // Collect them so that they can be updated out of m_RIDVectorLock. + // Collect them so that they can be updated out of m_RIDListLock. LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; uprocess.push_back(fi); // NOTE: safe loop, the incrementation was done before the loop body, @@ -1117,7 +1117,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { // Now, additionally for every failed link reset the TTL so that // they are set expired right now. - ScopedLock vg(m_RIDVectorLock); + ScopedLock vg(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (find_if(ufailed.begin(), ufailed.end(), LinkStatusInfo::HasID(i->m_iID)) != ufailed.end()) diff --git a/srtcore/queue.h b/srtcore/queue.h index 2d27c67b9..d59877983 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -320,32 +320,53 @@ class CHash CHash& operator=(const CHash&); }; +/// @brief A queue of sockets pending for connection. +/// It can be either a caller socket in a non-blocking mode +/// (the connection has to be handled in background), +/// or a socket in rendezvous connection mode. class CRendezvousQueue { public: - CRendezvousQueue(); - ~CRendezvousQueue(); + CRendezvousQueue(); + ~CRendezvousQueue(); public: - void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, - const srt::sync::steady_clock::time_point &ttl); - - void remove(const SRTSOCKET& id); - CUDT* retrieve(const sockaddr_any& addr, SRTSOCKET& id); - - void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response); + /// @brief Insert a new socket pending for connection (non-blocking caller or rendezvous). + /// @param id socket ID. + /// @param u pointer to a corresponding CUDT instance. + /// @param addr remote address to connect to. + /// @param ttl timepoint for connection attempt to expire. + void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, + const srt::sync::steady_clock::time_point &ttl); + + /// @brief Remove a socket from the connection pending list. + /// @param id socket ID. + void remove(const SRTSOCKET& id); + + /// @brief Locate a socket in the connection pending queue. + /// @param addr source address of the packet received over UDP (peer address). + /// @param id socket ID. + /// @return a pointer to CUDT instance retrieved, or NULL if nothing was found. + CUDT* retrieve(const sockaddr_any& addr, SRTSOCKET& id) const; + + /// @brief Update status of connections in the pending queue. + /// Stop connecting if TTL expires. Resend handshake request every 250 ms if no response from the peer. + /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. + /// @param cst target status for pending connection: reject or proceed. + /// @param response packet received from the UDP socket. + void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& response); private: struct CRL { - SRTSOCKET m_iID; // UDT socket ID (self) - CUDT* m_pUDT; // UDT instance - sockaddr_any m_PeerAddr;// UDT sonnection peer address + SRTSOCKET m_iID; // SRT socket ID (self) + CUDT* m_pUDT; // CUDT instance + sockaddr_any m_PeerAddr;// SRT sonnection peer address srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires }; std::list m_lRendezvousID; // The sockets currently in rendezvous mode - srt::sync::Mutex m_RIDVectorLock; + mutable srt::sync::Mutex m_RIDListLock; }; class CSndQueue From 917a71513e3d90e7c2c339c7a63f8f38b7af6e16 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 15:23:52 +0200 Subject: [PATCH 070/683] [core] Added CRendezvousQueue::qualifyToHandle. Now TTL of a socket pending for connection is checked on every update iteration -> more precise connection timeout. --- srtcore/queue.cpp | 276 +++++++++++++++++++--------------------------- srtcore/queue.h | 36 +++++- 2 files changed, 145 insertions(+), 167 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 2de1515dd..c2aa8d40d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -885,168 +885,22 @@ CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) cons return NULL; } -struct LinkStatusInfo +void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &pktIn) { - CUDT* u; - SRTSOCKET id; - int errorcode; - sockaddr_any peeraddr; - int token; + vector toRemove, toProcess; - struct HasID - { - SRTSOCKET id; - HasID(SRTSOCKET p): id(p) {} - bool operator()(const LinkStatusInfo& i) - { - return i.id == id; - } - }; -}; - -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &response) -{ - vector ufailed, uprocess; - -#if ENABLE_HEAVY_LOGGING - int debug_nupd = 0; - int debug_nrun = 0; - int debug_nfail = 0; -#endif - - { - ScopedLock vg(m_RIDListLock); - - if (m_lRendezvousID.empty()) - return; - - HLOGC(cnlog.Debug, - log << "updateConnStatus: updating after getting pkt id=" << response.m_iID - << " status: " << ConnectStatusStr(cst)); - - for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) - { - ++i_next; - // NOTE: This is a SAFE LOOP. - // Incrementation will be done at the end, after the processing did not - // REMOVE the currently processed element. When the element was removed, - // the iterator value for the next iteration will be taken from erase()'s result. - - // RST_AGAIN happens in case when the last attempt to read a packet from the UDP - // socket has read nothing. In this case it would be a repeated update, while - // still waiting for a response from the peer. When we have any other state here - // (most expectably CONN_CONTINUE or CONN_RENDEZVOUS, which means that a packet has - // just arrived in this iteration), do the update immetiately (in SRT this also - // involves additional incoming data interpretation, which wasn't the case in UDT). - - // Use "slow" cyclic responding in case when - // - RST_AGAIN (no packet was received for whichever socket) - // - a packet was received, but not for THIS socket - if (rst == RST_AGAIN || i->m_iID != response.m_iID) - { - // If no packet has been received from the peer, - // avoid sending too many requests, at most 1 request per 250ms - const steady_clock::time_point then = i->m_pUDT->m_tsLastReqTime; - const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration timeout_250ms = milliseconds_from(250); - const bool now_is_time = (now - then) > timeout_250ms; - HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << " then=" << FormatTime(then) - << " now=" << FormatTime(now) << " passed=" << count_microseconds(now - then) - << "<=> 250000 -- now's " << (now_is_time ? "" : "NOT ") << "the time"); - - if (!now_is_time) - continue; - } - - HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- sending update NOW."); - -#if ENABLE_HEAVY_LOGGING - ++debug_nrun; -#endif - - // XXX This looks like a loop that rolls in infinity without any sleeps - // inside and makes it once per about 50 calls send a hs conclusion - // for a randomly sampled rendezvous ID of a socket out of the list. - // Ok, probably the rendezvous ID should be just one so not much to - // sample from, but if so, why the container? - // - // This must be somehow fixed! - // - // Maybe the time should be simply checked once and the whole loop not - // done when "it's not the time"? - const steady_clock::time_point now = steady_clock::now(); - if (now >= i->m_tsTTL) - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID - << " removed - EXPIRED (" - // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. - << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") - << "). WILL REMOVE from queue"); - - // Set appropriate error information, but do not update yet. - // Exit the lock first. Collect objects to update them later. - int ccerror = SRT_ECONNREJ; - if (i->m_pUDT->m_RejectReason == SRT_REJ_UNKNOWN) - { - if (!is_zero(i->m_tsTTL)) - { - // Timer expired, set TIMEOUT forcefully - i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; - ccerror = SRT_ENOSERVER; - } - else - { - // In case of unknown reason, rejection should at least - // suggest error on the peer - i->m_pUDT->m_RejectReason = SRT_REJ_PEER; - } - } - - // The call to completeBrokenConnectionDependencies() cannot happen here - // under the lock of m_RIDListLock as it risks a deadlock. Collect it - // to update later. - LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; - ufailed.push_back(fi); - - // i_next was preincremented, but this is guaranteed to point to - // the element next to erased one. - i_next = m_lRendezvousID.erase(i); - continue; - } - else - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << std::fixed << (count_microseconds(i->m_tsTTL - now)/1000000.0) << "s of TTL)..."); - } - - // This queue is used only in case of Async mode (rendezvous or caller-listener). - // Synchronous connection requests are handled in startConnect() completely. - if (!i->m_pUDT->m_config.bSynRecving) - { - IF_HEAVY_LOGGING(++debug_nupd); - - // Collect them so that they can be updated out of m_RIDListLock. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; - uprocess.push_back(fi); - // NOTE: safe loop, the incrementation was done before the loop body, - // so the `i' node can be safely deleted. Just the body must end here. - continue; - } - else - { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " deemed SYNCHRONOUS, NOT UPDATING"); - } - } - - } + // If no socket were qualified for further handling, finish here. + // Otherwise toRemove and toProcess contain items to handle. + if (!qualifyToHandle(rst, cst, pktIn.m_iID, toRemove, toProcess)) + return; // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << uprocess.size() << " for processing, " - << ufailed.size() << " to close"); + HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " + << toRemove.size() << " to close"); - for (vector::iterator i = uprocess.begin(); i != uprocess.end(); ++i) + // Repeat (resend) connection request. + for (vector::iterator i = toProcess.begin(); i != toProcess.end(); ++i) { // IMPORTANT INFORMATION concerning changes towards UDT legacy. // In the UDT code there was no attempt to interpret any incoming data. @@ -1054,7 +908,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // m_ConnRes field, and m_ConnReq field was considered at this time accordingly updated. // Therefore this procedure did only one thing: craft a new handshake packet and send it. // In SRT this may also interpret extra data (extensions in case when Agent is Responder) - // and the `response` packet may sometimes contain no data. Therefore the passed `rst` + // and the `pktIn` packet may sometimes contain no data. Therefore the passed `rst` // must be checked to distinguish the call by periodic update (RST_AGAIN) from a call // due to have received the packet (RST_OK). // @@ -1065,7 +919,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (i->id != response.m_iID) + if (i->id != pktIn.m_iID) { read_st = RST_AGAIN; conn_st = CONN_AGAIN; @@ -1073,15 +927,14 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); - if (!i->u->processAsyncConnectRequest(read_st, conn_st, response, i->peeraddr)) + if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. LinkStatusInfo fi = *i; fi.errorcode = SRT_ECONNREJ; - ufailed.push_back(fi); + toRemove.push_back(fi); i->u->sendCtrl(UMSG_SHUTDOWN); - IF_HEAVY_LOGGING(++debug_nfail); } } @@ -1092,7 +945,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // they are moved to ClosedSockets and it is believed that this function will // not be held on mutexes that long. - for (vector::iterator i = ufailed.begin(); i != ufailed.end(); ++i) + for (vector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); /* @@ -1120,17 +973,110 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con ScopedLock vg(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { - if (find_if(ufailed.begin(), ufailed.end(), LinkStatusInfo::HasID(i->m_iID)) != ufailed.end()) + if (find_if(toRemove.begin(), toRemove.end(), LinkStatusInfo::HasID(i->m_iID)) != toRemove.end()) { LOGC(cnlog.Error, log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID << ". Setting TTL as EXPIRED."); i->m_tsTTL = steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration } } } +} + +bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, + int iDstSockID, vector& toRemove, vector& toProcess) +{ + ScopedLock vg(m_RIDListLock); + + if (m_lRendezvousID.empty()) + return false; // nothing to process. HLOGC(cnlog.Debug, - log << "updateConnStatus: " << debug_nupd << "/" << debug_nrun << " sockets updated (" - << (debug_nrun - debug_nupd) << " useless). REMOVED " << debug_nfail << " sockets."); + log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID + << " status: " << ConnectStatusStr(cst)); + + for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) + { + // Safe iterator to the next element. If the current element is erased, the iterator is updated again. + ++i_next; + + const steady_clock::time_point tsNow = steady_clock::now(); + + if (tsNow >= i->m_tsTTL) + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID + << " removed - EXPIRED (" + // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. + << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") + << "). WILL REMOVE from queue."); + + // Set appropriate error information, but do not update yet. + // Exit the lock first. Collect objects to update them later. + int ccerror = SRT_ECONNREJ; + if (i->m_pUDT->m_RejectReason == SRT_REJ_UNKNOWN) + { + if (!is_zero(i->m_tsTTL)) + { + // Timer expired, set TIMEOUT forcefully + i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; + ccerror = SRT_ENOSERVER; + } + else + { + // In case of unknown reason, rejection should at least + // suggest error on the peer + i->m_pUDT->m_RejectReason = SRT_REJ_PEER; + } + } + + // The call to completeBrokenConnectionDependencies() cannot happen here + // under the lock of m_RIDListLock as it risks a deadlock. + // Collect in 'toRemove' to update later. + LinkStatusInfo fi = { i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1 }; + toRemove.push_back(fi); + + // i_next was preincremented, but this is guaranteed to point to + // the element next to erased one. + i_next = m_lRendezvousID.erase(i); + continue; + } + else + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " + << std::fixed << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + } + + const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; + const steady_clock::time_point tsRepeat = tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). + + // A connection request is repeated every 250 ms if there was no response from the peer: + // - RST_AGAIN means no packet was received over UDP. + // - a packet was received, but not for THIS socket. + if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) + { + HLOGC(cnlog.Debug, + log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 + << " ms passed since last connection request."); + + continue; + } + + HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); + + // This queue is used only in case of Async mode (rendezvous or caller-listener). + // Synchronous connection requests are handled in startConnect() completely. + if (!i->m_pUDT->m_config.bSynRecving) + { + // Collect them so that they can be updated out of m_RIDListLock. + LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1 }; + toProcess.push_back(fi); + } + else + { + HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " is SYNCHRONOUS, NOT UPDATING"); + } + } + + return !toRemove.empty() || !toProcess.empty(); } // diff --git a/srtcore/queue.h b/srtcore/queue.h index d59877983..9c0affd5e 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -353,8 +353,40 @@ class CRendezvousQueue /// Stop connecting if TTL expires. Resend handshake request every 250 ms if no response from the peer. /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. - /// @param response packet received from the UDP socket. - void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& response); + /// @param pktIn packet received from the UDP socket. + void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn); + +private: + struct LinkStatusInfo + { + CUDT* u; + SRTSOCKET id; + int errorcode; + sockaddr_any peeraddr; + int token; + + struct HasID + { + SRTSOCKET id; + HasID(SRTSOCKET p) : id(p) {} + bool operator()(const LinkStatusInfo& i) + { + return i.id == id; + } + }; + }; + + /// @brief Qualify pending connections: + /// - Sockets with expired TTL go to the 'to_remove' list and removed from the queue straight away. + /// - If HS request is to be resent (resend 250 ms if no response from the peer) go to the 'to_process' list. + /// + /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. + /// @param cst target status for pending connection: reject or proceed. + /// @param iDstSockID destination socket ID of the received packet. + /// @param[in,out] toRemove stores sockets with expired TTL. + /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. + bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, int iDstSockID, + std::vector& toRemove, std::vector& toProcess); private: struct CRL From 87746453edcd37f2ae1987cfd052ca8f447c8738 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 11 May 2021 15:25:16 +0200 Subject: [PATCH 071/683] [tests] ConnectionTimeout.Nonblocking 500ms -> 300ms. Previously TTL was checked only every 250 ms, which resulted in a poor timeout precision. --- test/test_connection_timeout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 8b9bd31f8..241c5f1a8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -100,7 +100,7 @@ TEST_F(TestConnectionTimeout, Nonblocking) { EXPECT_EQ(conn_timeout, 3000); // Set connection timeout to 500 ms to reduce the test execution time - const int connection_timeout_ms = 500; + const int connection_timeout_ms = 300; EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS); const int yes = 1; From 0921da6cfd6e7c0f88e0a0400f05ded660af1e07 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 21 Apr 2021 16:24:13 +0200 Subject: [PATCH 072/683] [core] Fix DriftTracer: take RTT into account --- srtcore/buffer.cpp | 3 +- srtcore/buffer.h | 1 + srtcore/core.cpp | 2 +- srtcore/tsbpd_time.cpp | 107 +++++++++++++++++++++++++++++++++++++++-- srtcore/tsbpd_time.h | 12 +++-- srtcore/utilities.h | 6 ++- 6 files changed, 121 insertions(+), 10 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e2efcefff..b29aa9322 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1833,10 +1833,11 @@ void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, + int rtt, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { - return m_tsbpd.addDriftSample(timestamp_us, w_udrift, w_newtimebase); + return m_tsbpd.addDriftSample(timestamp_us, rtt, w_udrift, w_newtimebase); } int CRcvBuffer::readMsg(char* data, int len) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index d32e1d29e..7f7680b8d 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -414,6 +414,7 @@ class CRcvBuffer /// @param [out] w_udrift current drift value /// @param [out] w_newtimebase current TSBPD base time bool addRcvTsbPdDriftSample(uint32_t timestamp, + int rtt, duration& w_udrift, time_point& w_newtimebase); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index efc866164..a1985379d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8279,7 +8279,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival steady_clock::duration udrift(0); steady_clock::time_point newtimebase; const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), - (udrift), (newtimebase)); + rtt, (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 0fe07fb7c..8fedf82ea 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -19,17 +19,110 @@ using namespace srt::sync; namespace srt { +#if SRT_DEBUG_TRACE_DRIFT +class drift_logger +{ + using steady_clock = srt::sync::steady_clock; + +public: + drift_logger() {} + + ~drift_logger() + { + ScopedLock lck(m_mtx); + m_fout.close(); + } + + void trace(unsigned ackack_timestamp, + int rtt_us, + int64_t drift_sample, + int64_t drift, + int64_t overdrift, + const std::chrono::steady_clock::time_point& tsbpd_base) + { + using namespace srt::sync; + ScopedLock lck(m_mtx); + create_file(); + + // std::string str_tnow = srt::sync::FormatTime(steady_clock::now()); + // str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [STDY]' part + + std::string str_tbase = srt::sync::FormatTime(tsbpd_base); + str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part + + // m_fout << str_tnow << ","; + m_fout << count_microseconds(steady_clock::now() - m_start_time) << ","; + m_fout << ackack_timestamp << ","; + m_fout << rtt_us << ","; + m_fout << drift_sample << ","; + m_fout << drift << ","; + m_fout << overdrift << ","; + m_fout << str_tbase << "\n"; + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "usElapsedStd,usAckAckTimestampStd,"; + m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,TSBPDBase\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + m_start_time = srt::sync::steady_clock::now(); + std::string str_tnow = srt::sync::FormatTimeSys(m_start_time); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) + { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "drift_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; + srt::sync::steady_clock::time_point m_start_time; +}; + +drift_logger g_drift_logger; + +#endif // SRT_DEBUG_TRACE_DRIFT + bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, + int usRTTSample, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase) { if (!m_bTsbPdMode) return false; - + const time_point tsNow = steady_clock::now(); ScopedLock lck(m_mtxRW); - const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp); + + // Remember the first RTT sample measured. Ideally we need RTT0 - the one from the handshaking phase, + // because TSBPD base is initialized there. But HS-based RTT is not yet implemented. + // Take the first one assuming it is close to RTT0. + if (m_iFirstRTT == -1) + { + m_iFirstRTT = usRTTSample; + } + + // A change in network delay has to be taken into account. The only way to get some estimation of it + // is to estimate RTT change and assume that the change of the one way network delay is + // approximated by the half of the RTT change. + const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); + const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp) - tdRTTDelta; const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); @@ -53,6 +146,14 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, w_udrift = tdDrift; w_newtimebase = m_tsTsbPdTimeBase; +#if SRT_DEBUG_TRACE_DRIFT + g_drift_logger.trace(usPktTimestamp, + usRTTSample, + count_microseconds(tdDrift), + m_DriftTracer.drift(), + m_DriftTracer.overdrift(), + m_tsTsbPdTimeBase); +#endif return updated; } @@ -67,7 +168,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra // // This function is called in the HSREQ reception handler only. m_tsTsbPdTimeBase = timebase; - m_tdTsbPdDelay = delay; + m_tdTsbPdDelay = delay; } void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h index 5dcc7ff3a..b6cb770f5 100644 --- a/srtcore/tsbpd_time.h +++ b/srtcore/tsbpd_time.h @@ -30,7 +30,8 @@ class CTsbpdTime public: CTsbpdTime() - : m_bTsbPdMode(false) + : m_iFirstRTT(-1) + , m_bTsbPdMode(false) , m_tdTsbPdDelay(0) , m_bTsbPdWrapCheck(false) { @@ -61,13 +62,17 @@ class CTsbpdTime /// @brief Add new drift sample from an ACK-ACKACK pair. /// ACKACK packets are sent immediately (except for UDP buffering). + /// Therefore their timestamp roughly corresponds to the time of sending + /// and can be used to estimate clock drift. /// /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. + /// @param [in] usRTTSample RTT sample from an ACK-ACKACK pair. /// @param [out] w_udrift Current clock drift value. /// @param [out] w_newtimebase Current TSBPD base time. /// /// @return true if TSBPD base time has changed, false otherwise. bool addDriftSample(uint32_t pktTimestamp, + int usRTTSample, steady_clock::duration& w_udrift, steady_clock::time_point& w_newtimebase); @@ -116,8 +121,9 @@ class CTsbpdTime void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const; private: - bool m_bTsbPdMode; //< Rreceiver buffering and TSBPD is active when true. - duration m_tdTsbPdDelay; //< Negotiated buffering delay. + int m_iFirstRTT; // First measured RTT sample. + bool m_bTsbPdMode; // Receiver buffering and TSBPD is active when true. + duration m_tdTsbPdDelay; // Negotiated buffering delay. /// @brief Local time base for TsbPd. /// @note m_tsTsbPdTimeBase is changed in the following cases: diff --git a/srtcore/utilities.h b/srtcore/utilities.h index f0ad80271..7d73c8567 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -791,11 +791,13 @@ class DriftTracer m_qDriftSum += driftval; ++m_uDriftSpan; + // I moved it here to calculate accumulated overdrift. + if (CLEAR_ON_UPDATE) + m_qOverdrift = 0; + if (m_uDriftSpan < MAX_SPAN) return false; - if (CLEAR_ON_UPDATE) - m_qOverdrift = 0; // Calculate the median of all drift values. // In most cases, the divisor should be == MAX_SPAN. From a32e975f2403c8b2335e7ab9e77f21291a7e2b3c Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Tue, 18 May 2021 18:57:51 +1000 Subject: [PATCH 073/683] [build] Update for Android build scripts (#2009) - Remove obsolete declares and scripts; - Allow to set build parameters (ndk root, api level, archs set, versions) from the command line; - Use C++11 version of sync module by default. --- docs/build/build-android.md | 24 ++-------- scripts/build-android/README.md | 2 - scripts/build-android/build-android | 69 ++++++++++++++++++++++++++++ scripts/build-android/mkall | 71 ----------------------------- scripts/build-android/mksrt | 24 ++++++++-- scripts/build-android/mkssl | 16 ++----- scripts/build-android/packjni | 38 --------------- scripts/build-android/prepare_build | 36 --------------- 8 files changed, 100 insertions(+), 180 deletions(-) create mode 100755 scripts/build-android/build-android delete mode 100755 scripts/build-android/mkall delete mode 100755 scripts/build-android/packjni delete mode 100644 scripts/build-android/prepare_build diff --git a/docs/build/build-android.md b/docs/build/build-android.md index f2dec839e..af1685a61 100644 --- a/docs/build/build-android.md +++ b/docs/build/build-android.md @@ -2,31 +2,17 @@ **NOTE:** The scripts have been moved to [scripts/build-android](../../scripts/build-android/) folder. -## Establishing a Build Environment - -### Installing the Android NDK +## Install the NDK and CMake The Android NDK is required to build native modules for Android. +[Install and configure the NDK](https://developer.android.com/studio/projects/install-ndk) + Consider installing the latest version of cmake. The higher version of cmake the better. As of writing the current version of CMake is 3.18.4 You can download Cmake from the following website: [https://cmake.org/download](https://cmake.org/download/) -Download the NDK r19 or newer archive from the following site: -[Download the Android NDK on developer.android.com](https://developer.android.com/ndk/downloads/index.html) -To install the Android NDK, simply expand the archive in the folder where you want to install it. - -### OpenSSL - -Google removed openssl from Android 7+. You must build openssl libs by yourself. - -## Configure the NDK Path - -Edit the ```mkall``` script to configure NDK path. Set the ```NDK``` to the directory where the NDK is installed. - ## Build SRT for Android -Run ```/bin/bash mkall > build.log``` script. Libraries will be installed to ```./target-architecture/lib```. - -## Export SRT Libraries +Run ```./build-android -n /path/to/ndk```. E.g. ```./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529``` -Run ```/bin/bash packjni``` to generate ```jniLibs``` archive for Android Studio. +[Include prebuilt native libraries](https://developer.android.com/studio/projects/gradle-external-native-builds#jniLibs) from ```prebuilt``` folder into Android Studio project. diff --git a/scripts/build-android/README.md b/scripts/build-android/README.md index d225ed022..85277f2a6 100644 --- a/scripts/build-android/README.md +++ b/scripts/build-android/README.md @@ -1,5 +1,3 @@ ## Scripts for building SRT for Android -**NOTE:** The scripts have been moved from `docs/Android/` folder. Updating the paths might be required. - See [Building SRT for Android](../../docs/build/build-android.md) for the instructions. diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android new file mode 100755 index 000000000..622f525d2 --- /dev/null +++ b/scripts/build-android/build-android @@ -0,0 +1,69 @@ +#!/bin/sh + +echo_help() +{ + echo "Usage: $0 [options...]" + echo " -n Specify NDK root path for the build." + echo " -a Select target API level." + echo " -t Select target architectures." + echo " Android supports the following architectures: armeabi armeabi-v7a arm64-v8a x86 x86_64." + echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" + echo " -s Select a specific SRT tag. E.g. v1.4.3" + echo + echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" + echo +} + +# Init optional command line vars +NDK_ROOT="" +API_LEVEL=28 +BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" +OPENSSL_VERSION=1.1.1h +SRT_VERSION="" + +while getopts n:a:t:o:s: option +do + case "${option}" + in + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + s) SRT_VERSION=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + +echo_help + +if [ -z "$NDK_ROOT" ] ; then + echo "NDK directory not set." + exit 128 +else + if [ ! -d "$NDK_ROOT" ]; then + echo "NDK directory does not exist: $NDK_ROOT" + exit 128 + fi +fi + +# Determine the path of the executing script +BASE_DIR=$(readlink -f $0 | xargs dirname) + +$BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION + +if [ ! -d $BASE_DIR/srt ]; then + git clone https://github.com/Haivision/srt srt + if [ ! -z "$SRT_VERSION" ]; then + git -C $BASE_DIR/srt checkout $SRT_VERSION + fi +fi + +JNI_DIR=$BASE_DIR/prebuilt + +for build_target in $BUILD_TARGETS; do + git -C $BASE_DIR/srt clean -fd + $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/srt -i $BASE_DIR/$build_target + + mkdir -p $JNI_DIR/$build_target + cp $BASE_DIR/$build_target/lib/libsrt.so $JNI_DIR/$build_target/libsrt.so +done diff --git a/scripts/build-android/mkall b/scripts/build-android/mkall deleted file mode 100755 index dbfe386a3..000000000 --- a/scripts/build-android/mkall +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -NDK=/opt/android-ndk-r19c - -openssl_ver=1.1.1h -#srt_version=1.4.2 -#srt_branch=dev - -API_LEVEL=28 -# Determine the path of the executing script -BASE_DIR=$(readlink -f $0 | xargs dirname) - -#wget -N https://www.openssl.org/source/openssl-$openssl_ver.tar.gz - -if [ ! -d $BASE_DIR/srt ]; then - git clone https://github.com/Haivision/srt srt -# git -C $BASE_DIR/srt checkout v${srt_version} -# git -C $BASE_DIR/srt checkout -b $srt_branch origin/$srt_branch -fi - -# Clang, binutils, the sysroot, and other toolchain pieces are all installed to $NDK/toolchains/llvm/prebuilt/ -toolchain=$NDK/toolchains/llvm/prebuilt/linux-x86_64 - -# Cross-compiler tool prefix -declare -A target_hosts -target_hosts=( - [armeabi]=arm-linux-androideabi - [arm]=arm-linux-androideabi - [arm64]=aarch64-linux-android - [x86]=i686-linux-android - [x86_64]=x86_64-linux-android -) - -# Cross-compiler prefix for -clang and -clang++ has api version -declare -A target_api -target_api=( - [armeabi]=armv5te-linux-androideabi16 - [arm]=armv7a-linux-androideabi16 - [arm64]=aarch64-linux-android21 - [x86]=i686-linux-android16 - [x86_64]=x86_64-linux-android21 -) - -declare -A sysroots -sysroots=( - [armeabi]=$BASE_DIR/armeabi - [arm]=$BASE_DIR/armeabi-v7a - [arm64]=$BASE_DIR/arm64-v8a - [x86]=$BASE_DIR/x86 - [x86_64]=$BASE_DIR/x86_64 -) - -declare -A archabi -archabi=( - [armeabi]=armeabi - [arm]=armeabi-v7a - [arm64]=arm64-v8a - [x86]=x86 - [x86_64]=x86_64 -) - -$BASE_DIR/mkssl -n $NDK -o $openssl_ver -a $API_LEVEL - -for arch in armeabi arm arm64 x86 x86_64; do - #rm -rf $BASE_DIR/openssl-$openssl_ver - #tar xf $BASE_DIR/openssl-$openssl_ver.tar.gz - #$BASE_DIR/mkssl -t $toolchain -h ${target_hosts[$arch]} -a ${target_api[$arch]} -s $BASE_DIR/openssl-$openssl_ver -i ${sysroots[$arch]} - - git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -t $toolchain -h ${target_hosts[$arch]} -a ${target_api[$arch]} -s $BASE_DIR/srt -i ${sysroots[$arch]} -b ${archabi[$arch]} -n $NDK -l $API_LEVEL -done diff --git a/scripts/build-android/mksrt b/scripts/build-android/mksrt index 4d45755a4..2af96a7aa 100755 --- a/scripts/build-android/mksrt +++ b/scripts/build-android/mksrt @@ -1,7 +1,25 @@ -#!/bin/bash +#!/bin/sh -source prepare_build +while getopts s:i:t:n:a: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done -./configure --use-openssl-pc=OFF --CMAKE_PREFIX_PATH=$install_dir --CMAKE_INSTALL_PREFIX=$install_dir --CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$api_lev --CMAKE_ANDROID_NDK=$NDK_HOME --CMAKE_ANDROID_ARCH_ABI=$arch_abi + +cd $SRC_DIR +./configure --use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=true \ +--CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \ +--CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +--CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +--enable-c++11 --enable-stdcxx-sync \ +--enable-debug=2 --enable-logging=0 --enable-heavy-logging=0 --enable-apps=0 make make install diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index 726e4bb05..d0305fab7 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -1,27 +1,21 @@ #!/bin/sh -while getopts n:o:a: option +while getopts n:o:a:t: option do case "${option}" in - n) ndk_home=${OPTARG};; - o) ssl_ver=${OPTARG};; - a) api_lev=${OPTARG};; + n) ANDROID_NDK=${OPTARG};; + o) OPENSSL_VERSION=${OPTARG};; + a) API_LEVEL=${OPTARG};; + t) BUILD_TARGETS=${OPTARG};; *) twentytwo=${OPTARG};; esac done -ANDROID_NDK=$ndk_home -OPENSSL_VERSION=$ssl_ver - -API_LEVEL=$api_lev - BUILD_DIR=/tmp/openssl_android_build OUT_DIR=$(pwd) -BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" - if [ ! -d openssl-${OPENSSL_VERSION} ] then if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ] diff --git a/scripts/build-android/packjni b/scripts/build-android/packjni deleted file mode 100755 index 80e19ac80..000000000 --- a/scripts/build-android/packjni +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -BASE_DIR=$(readlink -f $0 | xargs dirname) - -declare -A sysroots -sysroots=( - [armeabi]=$BASE_DIR/armeabi - [arm]=$BASE_DIR/armeabi-v7a - [arm64]=$BASE_DIR/arm64-v8a - [x86]=$BASE_DIR/x86 - [x86_64]=$BASE_DIR/x86_64 -) - -srt_version=1.4.2 -jni_dir=$BASE_DIR/jniLibs - -declare -A jnilibs -jnilibs=( - [armeabi=$jni_dir/armeabi - [arm]=$jni_dir/armeabi-v7a - [arm64]=$jni_dir/arm64-v8a - [x86]=$jni_dir/x86 - [x86_64]=$jni_dir/x86_64 -) - -jni_dir=$BASE_DIR/jniLibs - -# Android < 6.0 has an issue with loading versioned libraries -# The issue is because of library so-name libsrt.so.1 - -for arch in armeabi arm arm64 x86 x86_64; do - mkdir -p ${jnilibs[$arch]} - cp ${sysroots[$arch]}/lib/libsrt.so ${jnilibs[$arch]}/libsrt.so - /usr/local/bin/patchelf --set-soname libsrt.so ${jnilibs[$arch]}/libsrt.so - objdump -p ${jnilibs[$arch]}/libsrt.so | grep libsrt.so -done - -zip -r libsrt_$(date +%Y-%m-%d).zip jniLibs diff --git a/scripts/build-android/prepare_build b/scripts/build-android/prepare_build deleted file mode 100644 index c0b2cd987..000000000 --- a/scripts/build-android/prepare_build +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -while getopts t:h:a:s:i:b:n:l: option -do - case "${option}" - in - t) toolchain_dir=${OPTARG};; - h) target_host=${OPTARG};; - a) target_host_api=${OPTARG};; - s) src_dir=${OPTARG};; - i) install_dir=$OPTARG;; - b) arch_abi=${OPTARG};; - n) NDK_HOME=${OPTARG};; - l) api_lev=${OPTARG};; - *) twentytwo=${OPTARG};; - esac -done - -# Add toolchain to the search path. -export PATH=$PATH:$toolchain_dir/bin - -# Tell configure what tools to use. -export AR=$target_host-ar -export AS=$target_host-as -export LD=$target_host-ld -export STRIP=$target_host-strip - -# Tell configure which android api to use. -export CC=$target_host_api-clang -export CXX=$target_host_api-clang++ - -# Tell configure what flags Android requires. -export CFLAGS="-fPIE -fPIC" -export LDFLAGS="-pie" - -cd $src_dir From 345bab754c0a7580bde4a975a2455e36c958628d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 18 May 2021 10:21:35 +0200 Subject: [PATCH 074/683] [core] Fixed version rejection for HSv4 caller --- srtcore/core.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a1985379d..f82f7875a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2400,7 +2400,17 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, } if (hs.m_iVersion < HS_VERSION_SRT1) + { + if (m_config.uMinimumPeerSrtVersion && m_config.uMinimumPeerSrtVersion >= SRT_VERSION_FEAT_HSv5) + { + m_RejectReason = SRT_REJ_VERSION; + // This means that a version with minimum 1.3.0 that features HSv5 is required, + // hence all HSv4 clients should be rejected. + LOGP(cnlog.Error, "interpretSrtHandshake: minimum peer version 1.3.0 (HSv5 only), rejecting HSv4 client"); + return false; + } return true; // do nothing + } // Anyway, check if the handshake contains any extra data. if (hspkt.getLength() <= CHandShake::m_iContentSize) @@ -10618,16 +10628,31 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // (or the above procedure failed) if (result == -1) { - HLOGC(cnlog.Debug, - log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" - << RequestTypeStr(hs.m_iReqType)); - size_t size = CHandShake::m_iContentSize; - hs.store_to((packet.m_pcData), (size)); - packet.setLength(size); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + if (hs.m_iVersion < HS_VERSION_SRT1) + { + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: HSv4 caller, sending SHUTDOWN after rejection with " + << RequestTypeStr(hs.m_iReqType)); + // The HSv4 clients do not interpret the error handshake response correctly. + // In order to really disallow them to connect there's needed the shutdown response. + CPacket rsp; + setPacketTS((rsp), steady_clock::now()); + rsp.pack(UMSG_SHUTDOWN); + rsp.m_iID = m_PeerID; + m_pSndQueue->sendto(addr, rsp); + } + else + { + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" + << RequestTypeStr(hs.m_iReqType)); + size_t size = CHandShake::m_iContentSize; + hs.store_to((packet.m_pcData), (size)); + packet.setLength(size); + packet.m_iID = id; + setPacketTS(packet, steady_clock::now()); + HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); + m_pSndQueue->sendto(addr, packet); + } } // new connection response should be sent in acceptAndRespond() // turn the socket writable if this is the first time when this was found out. From 7e5b3ee4d834522e1b65e0e0c475c89fda58a097 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 18 May 2021 11:49:33 +0200 Subject: [PATCH 075/683] [docs] Added buffer configuration guide (#1951) --- docs/API/configuration-guidelines.md | 110 +++++++++++++++++++++++++++ docs/README.md | 15 ++-- 2 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 docs/API/configuration-guidelines.md diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md new file mode 100644 index 000000000..f0566d7d0 --- /dev/null +++ b/docs/API/configuration-guidelines.md @@ -0,0 +1,110 @@ +# Configuration Guidelines + +## Receiver Buffer Size + +The receiver buffer can be configured with the [`SRTO_RCVBUF`](./API-socket-options.md#SRTO_RCVBUF) socket option. +Buffer size in bytes is expected to be passed in the `optval` argument of the `srt_setsockopt(..)` function. +However, internally the value will be converted into the number of packets stored in the receiver buffer. + +The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`]((./API-socket-options.md#SRTO_FC) socket option. +See issue [#700](https://github.com/Haivision/srt/issues/700). + +The default flow control window size is 25600 packets. It is approximately: + +- **270 Mbits** of payload in the default live streaming configuration with an SRT payload size of **1316 bytes**; +- **300 Mbits** of payload in the default file transfer configuration with an SRT payload size of **1456 bytes**. + +The default receiver buffer size is 8192 packets. It is approximately: +- **86 Mbits** of payload with the effective SRT payload size of **1316 bytes**. + +### Setting Receiver Buffer Size + +As already mentioned, the maximum allowed size of the receiver buffer is limited by the value of `SRTO_FC`. +When the `SRTO_RCVBUF` option value is set using the `srt_setsockopt(..)` function, +the provided size in bytes is internally converted to the corresponding size in packets +using the configured value of the `SRTO_MSS` option to estimate the maximum possible payload of a packet. + +The following function returns the buffer size in packets: + +```c++ +int getRbufSizePkts(int SRTO_RCVBUF, int SRTO_MSS, int SRTO_FC) +{ + // UDP header size is assumed to be 28 bytes + // 20 bytes IPv4 + 8 bytes of UDP + const int UDPHDR_SIZE = 28; + const in pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); + + return min(pkts, SRTO_FC); +} +``` + +If the value of `SRTO_RCVBUF` in packets exceeds `SRTO_FC`, then it is silently set to the value in bytes corresponding to `SRTO_FC`. +Therefore, to set higher values of `SRTO_RCVBUF` the value of `SRTO_FC` must be increased first. + +### Calculating Target Size in Packets + +The minimum size of the receiver buffer in packets can be calculated as follows: + +`pktsRBufSize = bps / 8 × (RTTsec + latency_sec) / bytePayloadSize` + +where + +- `bps` is the payload bitrate of the stream in bits per second; +- `RTTsec` is the RTT of the network connection in seconds; + +- `bytePayloadSize` is the expected size of the payload of the SRT data packet. + +If the whole remainder of the MTU is expected to be used, payload size is calculated as follows: + +`bytePayloadSize = MSS - 44` + +where + +- 44 is the size in bytes of an **IPv4** header: + - 20 bytes **IPv4** + - 8 bytes of UDP + - 16 bytes of SRT packet header. + +- `MSS` is the Maximum Segment Size (aka MTU); see `SRTO_MSS`. + +### Calculating Target Size to Set + +To determine the value to pass in `srt_setsockopt(..)` with `SRTO_RCVBUF` +the size in packets `pktsRBufSize` must be converted to the size in bytes +assuming the internal conversion of the `srt_setsockopt(..)` function. + +The target size of the payload stored by the receiver buffer would be: + +`SRTO_RCVBUF = pktsRBufSize × (SRTO_MSS - UDPHDR_SIZE)` + +where + +- `UDPHDR_SIZE` = 28 (20 bytes IPv4, 8 bytes of UDP) +- `SRTO_MSS` is the corresponding socket option value at the moment of setting `SRTO_RCVBUF`. + + +### Summing Up + + +```c++ + +auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS, int SRTO_FC) +{ + const int UDPHDR_SIZE = 28; + const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; + const int targetNumPackets = targetPayloadBytes / bytesPayloadSize; + const int targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); + return {targetNumPackets, targetSizeValue}; +} + +// Configuring + +const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS, SRTO_FC); + +int optval = fc; +int optlen = sizeof optval; +srt_setsockopt(sock, 0, SRTO_FC, (void*) &optval, optlen); + +optval = rcvbuf; +srt_setsockopt(sock, 0, SRTO_RCVBUF, (void*) &optval, optlen); +``` diff --git a/docs/README.md b/docs/README.md index 3e5ce72c1..55a632bcc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,13 +2,14 @@ ## SRT API Documents -| Document Title | Folder | File Name | Description | -| :-------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | -| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | -| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | -| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | -| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | -| | | | | +| Document Title | Folder | File Name | Description | +| :----------------------------------------------------- | :---------------------------- | :------------------------------------------------- | :--------------------------------------------------- | +| [SRT API](API/API.md) | [API](API/) | [API.md](API/API.md) | Detailed description of the SRT C API. | +| [SRT API Functions](API/API-functions.md) | [API](API/) | [API-functions.md](API/API-functions.md) | Reference document for SRT API functions. | +| [SRT API Socket Options](API/API-socket-options.md) | [API](API/) | [API-socket-options.md](API/API-socket-options.md) | Instructions and list of socket options for SRT API. | +| [SRT Statistics](API/statistics.md) | [API](API/) | [statistics.md](API/statistics.md) | How to use SRT socket and socket group statistics. | +| [Configuration Guidelines](API/configuration-guidelines.md) | [API](API/) | [configuration-guidelines.md](API/configuration-guidelines.md) | How to configure SRT buffers. | +| | | | | ## Build Instructions From d9150eaa8a791b6cba712ea68290e0beabed489d Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 May 2021 23:05:50 +0800 Subject: [PATCH 076/683] [core] Fixed DROPREQ by TTL packet drop (#2003) The sequence range to drop in the sender's drop request was 1 packet wider than needed. --- srtcore/buffer.cpp | 10 +++++++++- srtcore/core.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index b29aa9322..d42a57577 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -523,6 +523,10 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // by sequence number. Consider using some circular buffer. for (int i = 0; i < offset; ++i) p = p->m_pNext; +#if ENABLE_HEAVY_LOGGING + const int32_t first_seq = p->m_iSeqNo; + int32_t last_seq = p->m_iSeqNo; +#endif // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -546,6 +550,9 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time bool move = false; while (msgno == p->getMsgSeq()) { +#if ENABLE_HEAVY_LOGGING + last_seq = p->m_iSeqNo; +#endif if (p == m_pCurrBlock) move = true; p = p->m_pNext; @@ -555,7 +562,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, " << w_msglen << " messages to drop, up to " << msgno); + log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " + << w_msglen << " packets to drop, msgno=" << msgno); // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. // This means that in this case it should be written by the message sequence value only diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f82f7875a..a6dfb04cd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8881,10 +8881,11 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime { int32_t seqpair[2]; seqpair[0] = w_packet.m_iSeqNo; - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen); + SRT_ASSERT(msglen >= 1); + seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); HLOGC(qrlog.Debug, log << "IPE: loss-reported packets not found in SndBuf - requesting DROP: " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" + << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); @@ -8892,7 +8893,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime m_pSndLossList->removeUpTo(seqpair[1]); // skip all dropped packets - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, CSeqNo::incseq(seqpair[1])); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); continue; } @@ -11288,4 +11289,3 @@ void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) } #endif } - From 189b1710a08c543aaae20b6d88c76e60afbffc05 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 18 May 2021 14:26:00 +0200 Subject: [PATCH 077/683] [build] Fixed __MINGW32__ macro usage. Previously a custom __MINGW__ preprocessor definition was used. It was defined in udt.h, and not included everywhere. --- srtcore/platform_sys.h | 2 +- srtcore/srt.h | 6 +++--- srtcore/srt_compat.h | 2 +- srtcore/sync.cpp | 2 +- srtcore/udt.h | 8 -------- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index ed66e02bf..9fa2c7d32 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -30,7 +30,7 @@ #include #include -#ifndef __MINGW__ +#ifndef __MINGW32__ #include #endif diff --git a/srtcore/srt.h b/srtcore/srt.h index 92be107d2..60f3fc1e8 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -35,7 +35,7 @@ written by #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ // Explicitly define 32-bit and 64-bit numbers typedef __int32 int32_t; typedef __int64 int64_t; @@ -56,7 +56,7 @@ written by #else #define SRT_API #endif - #else // __MINGW__ + #else // __MINGW32__ #define SRT_API #endif #else @@ -152,7 +152,7 @@ typedef int32_t SRTSOCKET; static const int32_t SRTGROUP_MASK = (1 << 30); #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ typedef SOCKET SYSSOCKET; #else typedef int SYSSOCKET; diff --git a/srtcore/srt_compat.h b/srtcore/srt_compat.h index f7d072393..960c1b85a 100644 --- a/srtcore/srt_compat.h +++ b/srtcore/srt_compat.h @@ -22,7 +22,7 @@ written by #ifndef SRT_API #ifdef _WIN32 - #ifndef __MINGW__ + #ifndef __MINGW32__ #ifdef SRT_DYNAMIC #ifdef SRT_EXPORTS #define SRT_API __declspec(dllexport) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 7fb494256..c630f897d 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -227,7 +227,7 @@ bool srt::sync::CTimer::sleep_until(TimePoint tp) __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;"); #elif AMD64 __asm__ volatile ("nop; nop; nop; nop; nop;"); -#elif defined(_WIN32) && !defined(__MINGW__) +#elif defined(_WIN32) && !defined(__MINGW32__) __nop(); __nop(); __nop(); diff --git a/srtcore/udt.h b/srtcore/udt.h index 1bcc889ca..8d7d5eb87 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -82,13 +82,6 @@ modified by #define INCREMENT_THREAD_ITERATIONS() #endif -/* Obsolete way to define MINGW */ -#ifndef __MINGW__ -#if defined(__MINGW32__) || defined(__MINGW64__) -#define __MINGW__ 1 -#endif -#endif - #ifdef __cplusplus #include #include @@ -96,7 +89,6 @@ modified by #include #endif - //////////////////////////////////////////////////////////////////////////////// //if compiling on VC6.0 or pre-WindowsXP systems From 393a6c7041f6d883342a0bb6ca7c6d840f471229 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 12 May 2021 13:37:11 +0200 Subject: [PATCH 078/683] [core] Added srt::sync::genRandomInt(..) for a uniform way to get a random integer from the range. If C++11 is enabled, uniform_int_distribution and random_device are used. --- srtcore/api.cpp | 17 +--------- srtcore/channel.cpp | 8 +---- srtcore/congctl.cpp | 8 ++--- srtcore/core.h | 4 +-- srtcore/sync.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/sync.h | 12 +++++++ srtcore/utilities.h | 1 - test/test_sync.cpp | 21 ++++++++++++ 8 files changed, 120 insertions(+), 32 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5bd307125..542832f77 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -194,22 +194,7 @@ m_bGCStatus(false), m_ClosedSockets() { // Socket ID MUST start from a random value - // Note. Don't use CTimer here, because s_UDTUnited is a static instance of CUDTUnited - // with dynamic initialization (calling this constructor), while CTimer has - // a static member s_ullCPUFrequency with dynamic initialization. - // The order of initialization is not guaranteed. - timeval t; - - gettimeofday(&t, 0); - srand((unsigned int)t.tv_usec); - - const double rand1_0 = double(rand())/RAND_MAX; - - // Motivation: in case when rand() returns the value equal to RAND_MAX, - // rand1_0 == 1, so the below formula will be - // 1 + (MAX_SOCKET_VAL-1) * 1 = 1 + MAX_SOCKET_VAL - 1 = MAX_SOCKET_VAL - // which is the highest allowed value for the socket. - m_SocketIDGenerator = 1 + int((MAX_SOCKET_VAL-1) * rand1_0); + m_SocketIDGenerator = genRandomInt(1, MAX_SOCKET_VAL); m_SocketIDGenerator_init = m_SocketIDGenerator; // XXX An unlikely exception thrown from the below calls diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 865eaaabc..f78689a3a 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -562,12 +562,6 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (!packet.isControl()) { - if (dcounter == 0) - { - timeval tv; - gettimeofday(&tv, 0); - srand(tv.tv_usec & 0xFFFF); - } ++dcounter; if (flwcounter) @@ -581,7 +575,7 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > 8) { // Make a random number in the range between 8 and 24 - int rnd = rand() % 16 + SRT_TEST_FAKE_LOSS; + const int rnd = srt::sync::getRandomInt(8, 24); if (dcounter > rnd) { diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 0b00929b7..fdf9ddb0b 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -525,11 +525,9 @@ class FileCC : public SrtCongestionControlBase m_iLastDecSeq = m_parent->sndSeqNo(); - // remove global synchronization using randomization - srand(m_iLastDecSeq); - m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); - if (m_iDecRandom < 1) - m_iDecRandom = 1; + // remove global synchronization using randomization. + m_iDecRandom = genRandomInt(1, m_iAvgNAKNum); + SRT_ASSERT(m_iDecRandom >= 1); HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin << ", lastsentseqno=" << m_iLastDecSeq << ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin) diff --git a/srtcore/core.h b/srtcore/core.h index 97eda7436..1d75bbaea 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -401,9 +401,7 @@ class CUDT static int32_t generateISN() { using namespace srt::sync; - // Random Initial Sequence Number (normal mode) - srand((unsigned) count_microseconds(steady_clock::now().time_since_epoch())); - return (int32_t)(CSeqNo::m_iMaxSeqNo * (double(rand()) / RAND_MAX)); + return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } // For SRT_tsbpdLoop diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index c630f897d..820e0a7f9 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -18,11 +18,18 @@ #include "logging.h" #include "common.h" +// HAVE_CXX11 is defined in utilities.h, included with common.h. +// The following conditional inclusion must go after common.h. +#if HAVE_CXX11 +#include +#endif + namespace srt_logging { extern Logger inlog; } using namespace srt_logging; +using namespace std; namespace srt { @@ -267,3 +274,77 @@ bool srt::sync::CGlobEvent::waitForEvent() return g_Sync.lock_wait_for(milliseconds_from(10)); } +//////////////////////////////////////////////////////////////////////////////// +// +// Random +// +//////////////////////////////////////////////////////////////////////////////// + +namespace srt +{ +#if HAVE_CXX11 +static std::random_device& randomDevice() +{ + static std::random_device s_RandomDevice; + return s_RandomDevice; +} +#elif defined(_WIN32) && defined(__MINGW32__) +static void initRandSeed() +{ + const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); + srand((unsigned int) seed); +} +static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT; +#else + +static unsigned int genRandSeed() +{ + // Duration::count() does not depend on any global objects, + // therefore it is preferred over count)microseconds(..). + const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); + return (unsigned int) seed; +} + +static unsigned int* getRandSeed() +{ + static unsigned int s_uRandSeed = genRandSeed(); + return &s_uRandSeed; +} + +#endif +} + +int srt::sync::genRandomInt(int minVal, int maxVal) +{ + // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03. + // A mutex to protect simulteneout access to the random device. + // Thread-local storage could be used here instead to store the seed / random device. + // However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC), + // so sharing a single seed among threads should not impact the performance. + static sync::Mutex s_mtxRandomDevice; + sync::ScopedLock lck(s_mtxRandomDevice); +#if HAVE_CXX11 + uniform_int_distribution<> dis(minVal, maxVal); + return dis(randomDevice()); +#else +#if defined(__MINGW32__) + // No rand_r(..) for MinGW. + pthread_once(&s_InitRandSeedOnce, initRandSeed); + // rand() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive + // (i.e., the mathematical range [0, RAND_MAX]). + // Therefore, rand_0_1 belongs to [0.0, 1.0]. + const double rand_0_1 = double(rand()) / RAND_MAX; +#else // not __MINGW32__ + // rand_r(..) returns a pseudo-random integer in the range 0 to RAND_MAX inclusive + // (i.e., the mathematical range [0, RAND_MAX]). + // Therefore, rand_0_1 belongs to [0.0, 1.0]. + const double rand_0_1 = double(rand_r(getRandSeed())) / RAND_MAX; +#endif + + // Map onto [minVal, maxVal]. + // Note. The probablity to get maxVal as the result is minuscule. + const int res = minVal + static_cast((maxVal - minVal) * rand_0_1); + return res; +#endif // HAVE_CXX11 +} + diff --git a/srtcore/sync.h b/srtcore/sync.h index 7457c3df9..f25c5ca8d 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -835,6 +835,18 @@ void SetThreadLocalError(const CUDTException& e); /// @returns CUDTException pointer CUDTException& GetThreadLocalError(); +//////////////////////////////////////////////////////////////////////////////// +// +// Random distribution functions. +// +//////////////////////////////////////////////////////////////////////////////// + +/// Generate a uniform-distributed random integer from [minVal; maxVal]. +/// If HAVE_CXX11, uses std::uniform_distribution(std::random_device). +/// @param[in] minVal minimum allowed value of the resulting random number. +/// @param[in] maxVal maximum allowed value of the resulting random number. +int genRandomInt(int minVal, int maxVal); + } // namespace sync } // namespace srt diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 7d73c8567..caf882cd9 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -34,7 +34,6 @@ written by #define ATR_UNUSED #define ATR_DEPRECATED #endif - #if defined(__cplusplus) && __cplusplus > 199711L #define HAVE_CXX11 1 diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 47d23f7bc..950af154a 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -157,6 +157,27 @@ TEST(SyncDuration, OperatorMultIntEq) EXPECT_EQ(count_milliseconds(a), 7000); } +TEST(SyncRandom, GenRandomInt) +{ + vector mn(64); + + for (int i = 0; i < 2048; ++i) + { + const int rand_val = genRandomInt(0, 63); + ASSERT_GE(rand_val, 0); + ASSERT_LE(rand_val, 63); + ++mn[rand_val]; + } + + // Uncomment to see the distribution. + // for (size_t i = 0; i < mn.size(); ++i) + // { + // cout << i << '\t'; + // for (int j=0; j Date: Wed, 19 May 2021 12:23:31 +0200 Subject: [PATCH 079/683] [core] Fixed DROPREQ on NAK behind SndLastDataAck (#2011) --- srtcore/buffer.cpp | 5 +++-- srtcore/core.cpp | 8 +++++--- srtcore/srt.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index d42a57577..bce9922b6 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -183,7 +183,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) int32_t& w_msgno = w_mctrl.msgno; int32_t& w_seqno = w_mctrl.pktseq; int64_t& w_srctime = w_mctrl.srctime; - int& w_ttl = w_mctrl.msgttl; + const int& ttl = w_mctrl.msgttl; int size = len / m_iMSS; if ((len % m_iMSS) != 0) size++; @@ -254,7 +254,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) s->m_llSourceTime_us = w_srctime; s->m_tsOriginTime = time; s->m_tsRexmitTime = time_point(); - s->m_iTTL = w_ttl; + s->m_iTTL = ttl; // Rewrite the actual sending time back into w_srctime // so that the calling facilities can reuse it if (!w_srctime) @@ -542,6 +542,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // if found block is stale // (This is for messages that have declared TTL - messages that fail to be sent // before the TTL defined time comes, will be dropped). + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { int32_t msgno = p->getMsgSeq(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a6dfb04cd..f6c6cac0a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8848,9 +8848,11 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - seqpair[1] = m_iSndLastDataAck; + int32_t seqpair[2] = { + w_packet.m_iSeqNo, + CSeqNo::decseq(m_iSndLastDataAck) + }; + w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, log << "PEER reported LOSS not from the sending buffer - requesting DROP: " << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" diff --git a/srtcore/srt.h b/srtcore/srt.h index 60f3fc1e8..21e94879e 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -863,7 +863,7 @@ SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* op typedef struct SRT_MsgCtrl_ { int flags; // Left for future - int msgttl; // TTL for a message, default -1 (no TTL limitation) + int msgttl; // TTL for a message (millisec), default -1 (no TTL limitation) int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode. int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame int64_t srctime; // source time since epoch (usec), 0: use internal time (sender) From 4ddb68efaed2fa847ed02eac61a81e68bff7b94d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 10:45:24 +0200 Subject: [PATCH 080/683] [core] Applied clang-format on queue.h and cpp --- srtcore/queue.cpp | 328 +++++++++++++------------ srtcore/queue.h | 605 +++++++++++++++++++++++----------------------- 2 files changed, 482 insertions(+), 451 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index c2aa8d40d..ccc14b9b8 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -78,14 +78,14 @@ CUnitQueue::CUnitQueue() CUnitQueue::~CUnitQueue() { - CQEntry *p = m_pQEntry; + CQEntry* p = m_pQEntry; while (p != NULL) { delete[] p->m_pUnit; delete[] p->m_pBuffer; - CQEntry *q = p; + CQEntry* q = p; if (p == m_pLastQueue) p = NULL; else @@ -96,9 +96,9 @@ CUnitQueue::~CUnitQueue() int CUnitQueue::init(int size, int mss, int version) { - CQEntry *tempq = NULL; - CUnit * tempu = NULL; - char * tempb = NULL; + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; try { @@ -142,11 +142,11 @@ int CUnitQueue::increase() { // adjust/correct m_iCount int real_count = 0; - CQEntry *p = m_pQEntry; + CQEntry* p = m_pQEntry; while (p != NULL) { - CUnit *u = p->m_pUnit; - for (CUnit *end = u + p->m_iSize; u != end; ++u) + CUnit* u = p->m_pUnit; + for (CUnit* end = u + p->m_iSize; u != end; ++u) if (u->m_iFlag != CUnit::FREE) ++real_count; @@ -159,9 +159,9 @@ int CUnitQueue::increase() if (double(m_iCount) / m_iSize < 0.9) return -1; - CQEntry *tempq = NULL; - CUnit * tempu = NULL; - char * tempb = NULL; + CQEntry* tempq = NULL; + CUnit* tempu = NULL; + char* tempb = NULL; // all queues have the same size const int size = m_pQEntry->m_iSize; @@ -179,8 +179,8 @@ int CUnitQueue::increase() delete[] tempb; LOGC(rslog.Error, - log << "CUnitQueue:increase: failed to allocate " << size << " new units." - << " Current size=" << m_iSize); + log << "CUnitQueue:increase: failed to allocate " << size << " new units." + << " Current size=" << m_iSize); return -1; } @@ -208,7 +208,7 @@ int CUnitQueue::shrink() return -1; } -CUnit *CUnitQueue::getNextAvailUnit() +CUnit* CUnitQueue::getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); @@ -219,7 +219,7 @@ CUnit *CUnitQueue::getNextAvailUnit() int units_checked = 0; do { - const CUnit *end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize; + const CUnit* end = m_pCurrQueue->m_pUnit + m_pCurrQueue->m_iSize; for (; m_pAvailUnit != end; ++m_pAvailUnit, ++units_checked) { if (m_pAvailUnit->m_iFlag == CUnit::FREE) @@ -237,7 +237,7 @@ CUnit *CUnitQueue::getNextAvailUnit() return NULL; } -void CUnitQueue::makeUnitFree(CUnit *unit) +void CUnitQueue::makeUnitFree(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); @@ -245,7 +245,7 @@ void CUnitQueue::makeUnitFree(CUnit *unit) --m_iCount; } -void CUnitQueue::makeUnitGood(CUnit *unit) +void CUnitQueue::makeUnitGood(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); @@ -262,7 +262,7 @@ CSndUList::CSndUList() , m_pWindowCond(NULL) , m_pTimer(NULL) { - m_pHeap = new CSNode *[m_iArrayLength]; + m_pHeap = new CSNode*[m_iArrayLength]; } CSndUList::~CSndUList() @@ -307,7 +307,7 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) return -1; - CUDT *u = m_pHeap[0]->m_pUDT; + CUDT* u = m_pHeap[0]->m_pUDT; remove_(u); #define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " @@ -337,7 +337,7 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) return 1; } -void CSndUList::remove(const CUDT *u) +void CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); @@ -356,18 +356,18 @@ steady_clock::time_point CSndUList::getNextProcTime() void CSndUList::realloc_() { - CSNode **temp = NULL; + CSNode** temp = NULL; try { - temp = new CSNode *[2 * m_iArrayLength]; + temp = new CSNode*[2 * m_iArrayLength]; } catch (...) { throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); } - memcpy((temp), m_pHeap, sizeof(CSNode *) * m_iArrayLength); + memcpy((temp), m_pHeap, sizeof(CSNode*) * m_iArrayLength); m_iArrayLength *= 2; delete[] m_pHeap; m_pHeap = temp; @@ -384,7 +384,7 @@ void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) { - CSNode *n = u->m_pSNode; + CSNode* n = u->m_pSNode; // do not insert repeated node if (n->m_iHeapLoc >= 0) @@ -394,7 +394,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT m_iLastEntry++; m_pHeap[m_iLastEntry] = n; - n->m_tsTimeStamp = ts; + n->m_tsTimeStamp = ts; int q = m_iLastEntry; int p = q; @@ -424,7 +424,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT void CSndUList::remove_(const CUDT* u) { - CSNode *n = u->m_pSNode; + CSNode* n = u->m_pSNode; if (n->m_iHeapLoc >= 0) { @@ -493,14 +493,20 @@ CSndQueue::~CSndQueue() delete m_pSndUList; } -int CSndQueue::ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } -int CSndQueue::sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } +int CSndQueue::ioctlQuery(int type) const +{ + return m_pChannel->ioctlQuery(type); +} +int CSndQueue::sockoptQuery(int level, int type) const +{ + return m_pChannel->sockoptQuery(level, type); +} #if ENABLE_LOGGING - int CSndQueue::m_counter = 0; +int CSndQueue::m_counter = 0; #endif -void CSndQueue::init(CChannel *c, CTimer *t) +void CSndQueue::init(CChannel* c, CTimer* t) { m_pChannel = c; m_pTimer = t; @@ -512,7 +518,7 @@ void CSndQueue::init(CChannel *c, CTimer *t) #if ENABLE_LOGGING ++m_counter; const std::string thrname = "SRT:SndQ:w" + Sprint(m_counter); - const char* thname = thrname.c_str(); + const char* thname = thrname.c_str(); #else const char* thname = "SRT:SndQ"; #endif @@ -520,17 +526,26 @@ void CSndQueue::init(CChannel *c, CTimer *t) throw CUDTException(MJ_SYSTEMRES, MN_THREAD); } -int CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : -1; } +int CSndQueue::getIpTTL() const +{ + return m_pChannel ? m_pChannel->getIpTTL() : -1; +} -int CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } +int CSndQueue::getIpToS() const +{ + return m_pChannel ? m_pChannel->getIpToS() : -1; +} #ifdef SRT_ENABLE_BINDTODEVICE -bool CSndQueue::getBind(char* dst, size_t len) const { return m_pChannel ? m_pChannel->getBind(dst, len) : false; } +bool CSndQueue::getBind(char* dst, size_t len) const +{ + return m_pChannel ? m_pChannel->getBind(dst, len) : false; +} #endif -void *CSndQueue::worker(void *param) +void* CSndQueue::worker(void* param) { - CSndQueue *self = (CSndQueue *)param; + CSndQueue* self = (CSndQueue*)param; #if ENABLE_LOGGING THREAD_STATE_INIT(("SRT:SndQ:w" + Sprint(m_counter)).c_str()); @@ -558,8 +573,8 @@ void *CSndQueue::worker(void *param) self->m_WorkerStats.lNotReadyTs++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ - UniqueLock windlock (self->m_WindowLock); - CSync windsync (self->m_WindowCond, windlock); + UniqueLock windlock(self->m_WindowLock); + CSync windsync(self->m_WindowCond, windlock); // wait here if there is no sockets with data to be sent THREAD_PAUSED(); @@ -646,9 +661,9 @@ CRcvUList::CRcvUList() CRcvUList::~CRcvUList() {} -void CRcvUList::insert(const CUDT *u) +void CRcvUList::insert(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; n->m_tsTimeStamp = steady_clock::now(); if (NULL == m_pUList) @@ -667,9 +682,9 @@ void CRcvUList::insert(const CUDT *u) m_pLast = n; } -void CRcvUList::remove(const CUDT *u) +void CRcvUList::remove(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; @@ -698,9 +713,9 @@ void CRcvUList::remove(const CUDT *u) n->m_pNext = n->m_pPrev = NULL; } -void CRcvUList::update(const CUDT *u) +void CRcvUList::update(const CUDT* u) { - CRNode *n = u->m_pRNode; + CRNode* n = u->m_pRNode; if (!n->m_bOnList) return; @@ -739,10 +754,10 @@ CHash::~CHash() { for (int i = 0; i < m_iHashSize; ++i) { - CBucket *b = m_pBucket[i]; + CBucket* b = m_pBucket[i]; while (NULL != b) { - CBucket *n = b->m_pNext; + CBucket* n = b->m_pNext; delete b; b = n; } @@ -753,7 +768,7 @@ CHash::~CHash() void CHash::init(int size) { - m_pBucket = new CBucket *[size]; + m_pBucket = new CBucket*[size]; for (int i = 0; i < size; ++i) m_pBucket[i] = NULL; @@ -761,10 +776,10 @@ void CHash::init(int size) m_iHashSize = size; } -CUDT *CHash::lookup(int32_t id) +CUDT* CHash::lookup(int32_t id) { // simple hash function (% hash table size); suitable for socket descriptors - CBucket *b = m_pBucket[id % m_iHashSize]; + CBucket* b = m_pBucket[id % m_iHashSize]; while (NULL != b) { @@ -776,11 +791,11 @@ CUDT *CHash::lookup(int32_t id) return NULL; } -void CHash::insert(int32_t id, CUDT *u) +void CHash::insert(int32_t id, CUDT* u) { - CBucket *b = m_pBucket[id % m_iHashSize]; + CBucket* b = m_pBucket[id % m_iHashSize]; - CBucket *n = new CBucket; + CBucket* n = new CBucket; n->m_iID = id; n->m_pUDT = u; n->m_pNext = b; @@ -790,8 +805,8 @@ void CHash::insert(int32_t id, CUDT *u) void CHash::remove(int32_t id) { - CBucket *b = m_pBucket[id % m_iHashSize]; - CBucket *p = NULL; + CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* p = NULL; while (NULL != b) { @@ -824,26 +839,28 @@ CRendezvousQueue::~CRendezvousQueue() m_lRendezvousID.clear(); } -void CRendezvousQueue::insert( - const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) +void CRendezvousQueue::insert(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const steady_clock::time_point& ttl) { ScopedLock vg(m_RIDListLock); CRL r; - r.m_iID = id; - r.m_pUDT = u; + r.m_iID = id; + r.m_pUDT = u; r.m_PeerAddr = addr; - r.m_tsTTL = ttl; + r.m_tsTTL = ttl; m_lRendezvousID.push_back(r); - HLOGC(cnlog.Debug, log << "RID: adding socket @" << id << " for address: " << addr.str() - << " expires: " << FormatTime(ttl) - << " (total connectors: " << m_lRendezvousID.size() << ")"); + HLOGC(cnlog.Debug, + log << "RID: adding socket @" << id << " for address: " << addr.str() << " expires: " << FormatTime(ttl) + << " (total connectors: " << m_lRendezvousID.size() << ")"); } -void CRendezvousQueue::remove(const SRTSOCKET &id) +void CRendezvousQueue::remove(const SRTSOCKET& id) { - ScopedLock lkv (m_RIDListLock); + ScopedLock lkv(m_RIDListLock); for (list::iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { @@ -857,35 +874,36 @@ void CRendezvousQueue::remove(const SRTSOCKET &id) CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { - ScopedLock vg(m_RIDListLock); + ScopedLock vg(m_RIDListLock); // TODO: optimize search for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) { - HLOGC(cnlog.Debug, log << "RID: found id @" << i->m_iID << " while looking for " - << (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") - << i->m_PeerAddr.str()); + HLOGC(cnlog.Debug, + log << "RID: found id @" << i->m_iID << " while looking for " + << (w_id ? "THIS ID FROM " : "A NEW CONNECTION FROM ") << i->m_PeerAddr.str()); w_id = i->m_iID; return i->m_pUDT; } } #if ENABLE_HEAVY_LOGGING - std::ostringstream spec; - if (w_id == 0) - spec << "A NEW CONNECTION REQUEST"; - else - spec << " AGENT @" << w_id; - HLOGC(cnlog.Debug, log << "RID: NO CONNECTOR FOR ADR:" << addr.str() - << " while looking for " << spec.str() << " (" << m_lRendezvousID.size() << " connectors total)"); + std::ostringstream spec; + if (w_id == 0) + spec << "A NEW CONNECTION REQUEST"; + else + spec << " AGENT @" << w_id; + HLOGC(cnlog.Debug, + log << "RID: NO CONNECTOR FOR ADR:" << addr.str() << " while looking for " << spec.str() << " (" + << m_lRendezvousID.size() << " connectors total)"); #endif return NULL; } -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket &pktIn) +void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) { vector toRemove, toProcess; @@ -896,8 +914,9 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " - << toRemove.size() << " to close"); + HLOGC(cnlog.Debug, + log << "updateConnStatus: collected " << toProcess.size() << " for processing, " << toRemove.size() + << " to close"); // Repeat (resend) connection request. for (vector::iterator i = toProcess.begin(); i != toProcess.end(); ++i) @@ -925,18 +944,18 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con conn_st = CONN_AGAIN; } - HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); + HLOGC(cnlog.Debug, + log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. LinkStatusInfo fi = *i; - fi.errorcode = SRT_ECONNREJ; + fi.errorcode = SRT_ECONNREJ; toRemove.push_back(fi); i->u->sendCtrl(UMSG_SHUTDOWN); } - } // NOTE: it is "believed" here that all CUDT objects will not be @@ -962,7 +981,8 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con // be normally closed by the application, after it is done with them. // app can call any UDT API to learn the connection_broken error - CUDT::s_UDTUnited.m_EPoll.update_events(i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + CUDT::s_UDTUnited.m_EPoll.update_events( + i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); i->u->completeBrokenConnectionDependencies(i->errorcode); } @@ -975,15 +995,21 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con { if (find_if(toRemove.begin(), toRemove.end(), LinkStatusInfo::HasID(i->m_iID)) != toRemove.end()) { - LOGC(cnlog.Error, log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID << ". Setting TTL as EXPIRED."); - i->m_tsTTL = steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration + LOGC(cnlog.Error, + log << "updateConnStatus: processAsyncConnectRequest FAILED on @" << i->m_iID + << ". Setting TTL as EXPIRED."); + i->m_tsTTL = + steady_clock::time_point(); // Make it expire right now, will be picked up at the next iteration } } } } -bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, - int iDstSockID, vector& toRemove, vector& toProcess) +bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, + EConnectStatus cst SRT_ATR_UNUSED, + int iDstSockID, + vector& toRemove, + vector& toProcess) { ScopedLock vg(m_RIDListLock); @@ -991,8 +1017,8 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A return false; // nothing to process. HLOGC(cnlog.Debug, - log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID - << " status: " << ConnectStatusStr(cst)); + log << "updateConnStatus: updating after getting pkt with DST socket ID @" << iDstSockID + << " status: " << ConnectStatusStr(cst)); for (list::iterator i = m_lRendezvousID.begin(), i_next = i; i != m_lRendezvousID.end(); i = i_next) { @@ -1003,11 +1029,11 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A if (tsNow >= i->m_tsTTL) { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID - << " removed - EXPIRED (" - // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. - << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") - << "). WILL REMOVE from queue."); + HLOGC(cnlog.Debug, + log << "RID: socket @" << i->m_iID + << " removed - EXPIRED (" + // The "enforced on FAILURE" is below when processAsyncConnectRequest failed. + << (is_zero(i->m_tsTTL) ? "enforced on FAILURE" : "passed TTL") << "). WILL REMOVE from queue."); // Set appropriate error information, but do not update yet. // Exit the lock first. Collect objects to update them later. @@ -1018,7 +1044,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A { // Timer expired, set TIMEOUT forcefully i->m_pUDT->m_RejectReason = SRT_REJ_TIMEOUT; - ccerror = SRT_ENOSERVER; + ccerror = SRT_ENOSERVER; } else { @@ -1031,7 +1057,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A // The call to completeBrokenConnectionDependencies() cannot happen here // under the lock of m_RIDListLock as it risks a deadlock. // Collect in 'toRemove' to update later. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1 }; + LinkStatusInfo fi = {i->m_pUDT, i->m_iID, ccerror, i->m_PeerAddr, -1}; toRemove.push_back(fi); // i_next was preincremented, but this is guaranteed to point to @@ -1041,12 +1067,14 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A } else { - HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << std::fixed << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + HLOGC(cnlog.Debug, + log << "RID: socket @" << i->m_iID << " still active (remaining " << std::fixed + << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; - const steady_clock::time_point tsRepeat = tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). + const steady_clock::time_point tsRepeat = + tsLastReq + milliseconds_from(250); // Repeat connection request (send HS). // A connection request is repeated every 250 ms if there was no response from the peer: // - RST_AGAIN means no packet was received over UDP. @@ -1054,20 +1082,21 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_A if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 + << " ms passed since last connection request."); continue; } - HLOGC(cnlog.Debug, log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); + HLOGC(cnlog.Debug, + log << "RID:@" << i->m_iID << " cst=" << ConnectStatusStr(cst) << " -- repeating connection request."); // This queue is used only in case of Async mode (rendezvous or caller-listener). // Synchronous connection requests are handled in startConnect() completely. if (!i->m_pUDT->m_config.bSynRecving) { // Collect them so that they can be updated out of m_RIDListLock. - LinkStatusInfo fi = { i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1 }; + LinkStatusInfo fi = {i->m_pUDT, i->m_iID, SRT_SUCCESS, i->m_PeerAddr, -1}; toProcess.push_back(fi); } else @@ -1116,11 +1145,11 @@ CRcvQueue::~CRcvQueue() delete m_pRendezvousQueue; // remove all queued messages - for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) { while (!i->second.empty()) { - CPacket *pkt = i->second.front(); + CPacket* pkt = i->second.front(); delete[] pkt->m_pcData; delete pkt; i->second.pop(); @@ -1129,15 +1158,14 @@ CRcvQueue::~CRcvQueue() } #if ENABLE_LOGGING - int CRcvQueue::m_counter = 0; +int CRcvQueue::m_counter = 0; #endif - -void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel *cc, CTimer *t) +void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) { m_szPayloadSize = payload; - m_UnitQueue.init(qsize, (int) payload, version); + m_UnitQueue.init(qsize, (int)payload, version); m_pHash = new CHash; m_pHash->init(hsize); @@ -1161,9 +1189,9 @@ void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel } } -void *CRcvQueue::worker(void *param) +void* CRcvQueue::worker(void* param) { - CRcvQueue * self = (CRcvQueue *)param; + CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->m_UnitQueue.getIPversion()); int32_t id = 0; @@ -1173,7 +1201,7 @@ void *CRcvQueue::worker(void *param) THREAD_STATE_INIT("SRT:RcvQ:worker"); #endif - CUnit * unit = 0; + CUnit* unit = 0; EConnectStatus cst = CONN_AGAIN; while (!self->m_bClosing) { @@ -1243,12 +1271,13 @@ void *CRcvQueue::worker(void *param) // OTHERWISE: this is an "AGAIN" situation. No data was read, but the process should continue. // take care of the timing event for all UDT sockets - const steady_clock::time_point curtime_minus_syn = steady_clock::now() - microseconds_from(CUDT::COMM_SYN_INTERVAL_US); + const steady_clock::time_point curtime_minus_syn = + steady_clock::now() - microseconds_from(CUDT::COMM_SYN_INTERVAL_US); - CRNode *ul = self->m_pRcvUList->m_pUList; + CRNode* ul = self->m_pRcvUList->m_pUList; while ((NULL != ul) && (ul->m_tsTimeStamp < curtime_minus_syn)) { - CUDT *u = ul->m_pUDT; + CUDT* u = ul->m_pUDT; if (u->m_bConnected && !u->m_bBroken && !u->m_bClosing) { @@ -1304,7 +1333,7 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad // check waiting list, if new socket, insert it to the list while (ifNewEntry()) { - CUDT *ne = getNewEntry(); + CUDT* ne = getNewEntry(); if (ne) { HLOGC(qrlog.Debug, @@ -1344,9 +1373,9 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad if (rst == RST_OK) { w_id = w_unit->m_Packet.m_iID; - HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() - << " BOUND=" << m_pChannel->bindAddressAny().str() - << " " << w_unit->m_Packet.Info()); + HLOGC(qrlog.Debug, + log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " + << w_unit->m_Packet.Info()); } return rst; } @@ -1354,20 +1383,18 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) { HLOGC(cnlog.Debug, - log << "Got sockID=0 from " << addr.str() - << " - trying to resolve it as a connection request..."); + log << "Got sockID=0 from " << addr.str() << " - trying to resolve it as a connection request..."); // Introduced protection because it may potentially happen // that another thread could have closed the socket at // the same time and inject a bug between checking the // pointer for NULL and using it. - int listener_ret = SRT_REJ_UNKNOWN; - bool have_listener = false; + int listener_ret = SRT_REJ_UNKNOWN; + bool have_listener = false; { ScopedLock cg(m_LSLock); if (m_pListener) { - LOGC(cnlog.Note, - log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); + LOGC(cnlog.Note, log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); // This function does return a code, but it's hard to say as to whether @@ -1398,7 +1425,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { - CUDT *u = m_pHash->lookup(id); + CUDT* u = m_pHash->lookup(id); if (!u) { // Pass this to either async rendezvous connection, @@ -1412,8 +1439,8 @@ EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, if (addr != u->m_PeerAddr) { HLOGC(cnlog.Debug, - log << CONID() << "Packet for SID=" << id << " asoc with " << u->m_PeerAddr.str() - << " received from " << addr.str() << " (CONSIDERED ATTACK ATTEMPT)"); + log << CONID() << "Packet for SID=" << id << " asoc with " << u->m_PeerAddr.str() << " received from " + << addr.str() << " (CONSIDERED ATTACK ATTEMPT)"); // This came not from the address that is the peer associated // with the socket. Ignore it. return CONN_AGAIN; @@ -1454,7 +1481,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // stored in the rendezvous queue (see CRcvQueue::registerConnector) // or simply 0, but then at least the address must match one of these. // If the id was 0, it will be set to the actual socket ID of the returned CUDT. - CUDT *u = m_pRendezvousQueue->retrieve(addr, (id)); + CUDT* u = m_pRendezvousQueue->retrieve(addr, (id)); if (!u) { // this socket is then completely unknown to the system. @@ -1525,7 +1552,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c // that we KNOW (by the cst == CONN_ACCEPT result) that the socket should be inserted // into the pending anteroom. - CUDT *ne = getNewEntry(); // This function actuall removes the entry and returns it. + CUDT* ne = getNewEntry(); // This function actuall removes the entry and returns it. // This **should** now always return a non-null value, but check it first // because if this accidentally isn't true, the call to worker_ProcessAddressedPacket will // result in redirecting it to here and so on until the call stack overflow. In case of @@ -1597,10 +1624,10 @@ void CRcvQueue::stopWorker() int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) { - UniqueLock bufferlock (m_BufferLock); - CSync buffercond (m_BufferCond, bufferlock); + UniqueLock bufferlock(m_BufferLock); + CSync buffercond(m_BufferCond, bufferlock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1617,7 +1644,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) } // retrieve the earliest packet - CPacket *newpkt = i->second.front(); + CPacket* newpkt = i->second.front(); if (w_packet.getLength() < newpkt->getLength()) { @@ -1649,7 +1676,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) return (int)w_packet.getLength(); } -int CRcvQueue::setListener(CUDT *u) +int CRcvQueue::setListener(CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1660,7 +1687,7 @@ int CRcvQueue::setListener(CUDT *u) return 0; } -void CRcvQueue::removeListener(const CUDT *u) +void CRcvQueue::removeListener(const CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1668,21 +1695,24 @@ void CRcvQueue::removeListener(const CUDT *u) m_pListener = NULL; } -void CRcvQueue::registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) +void CRcvQueue::registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const steady_clock::time_point& ttl) { HLOGC(cnlog.Debug, log << "registerConnector: adding @" << id << " addr=" << addr.str() << " TTL=" << FormatTime(ttl)); m_pRendezvousQueue->insert(id, u, addr, ttl); } -void CRcvQueue::removeConnector(const SRTSOCKET &id) +void CRcvQueue::removeConnector(const SRTSOCKET& id) { HLOGC(cnlog.Debug, log << "removeConnector: removing @" << id); m_pRendezvousQueue->remove(id); ScopedLock bufferlock(m_BufferLock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { HLOGC(cnlog.Debug, @@ -1697,34 +1727,37 @@ void CRcvQueue::removeConnector(const SRTSOCKET &id) } } -void CRcvQueue::setNewEntry(CUDT *u) +void CRcvQueue::setNewEntry(CUDT* u) { HLOGC(cnlog.Debug, log << CUDTUnited::CONID(u->m_SocketID) << "setting socket PENDING FOR CONNECTION"); ScopedLock listguard(m_IDLock); m_vNewEntry.push_back(u); } -bool CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } +bool CRcvQueue::ifNewEntry() +{ + return !(m_vNewEntry.empty()); +} -CUDT *CRcvQueue::getNewEntry() +CUDT* CRcvQueue::getNewEntry() { ScopedLock listguard(m_IDLock); if (m_vNewEntry.empty()) return NULL; - CUDT *u = (CUDT *)*(m_vNewEntry.begin()); + CUDT* u = (CUDT*)*(m_vNewEntry.begin()); m_vNewEntry.erase(m_vNewEntry.begin()); return u; } -void CRcvQueue::storePkt(int32_t id, CPacket *pkt) +void CRcvQueue::storePkt(int32_t id, CPacket* pkt) { - UniqueLock bufferlock (m_BufferLock); - CSync passcond (m_BufferCond, bufferlock); + UniqueLock bufferlock(m_BufferLock); + CSync passcond(m_BufferCond, bufferlock); - map >::iterator i = m_mBuffer.find(id); + map >::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1741,10 +1774,9 @@ void CRcvQueue::storePkt(int32_t id, CPacket *pkt) } } - void CMultiplexer::destroy() { - // Reverse order of the assigned + // Reverse order of the assigned delete m_pRcvQueue; delete m_pSndQueue; delete m_pTimer; diff --git a/srtcore/queue.h b/srtcore/queue.h index 9c0affd5e..a412fd516 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -50,7 +50,6 @@ modified by Haivision Systems Inc. *****************************************************************************/ - #ifndef INC_SRT_QUEUE_H #define INC_SRT_QUEUE_H @@ -69,255 +68,257 @@ class CChannel; struct CUnit { - CPacket m_Packet; // packet - enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 }; - Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped + CPacket m_Packet; // packet + enum Flag + { + FREE = 0, + GOOD = 1, + PASSACK = 2, + DROPPED = 3 + }; + Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped }; class CUnitQueue { public: + CUnitQueue(); + ~CUnitQueue(); - CUnitQueue(); - ~CUnitQueue(); - -public: // Storage size operations - - /// Initialize the unit queue. - /// @param [in] size queue size - /// @param [in] mss maximum segment size - /// @param [in] version IP version - /// @return 0: success, -1: failure. +public: // Storage size operations + /// Initialize the unit queue. + /// @param [in] size queue size + /// @param [in] mss maximum segment size + /// @param [in] version IP version + /// @return 0: success, -1: failure. - int init(int size, int mss, int version); + int init(int size, int mss, int version); - /// Increase (double) the unit queue size. - /// @return 0: success, -1: failure. + /// Increase (double) the unit queue size. + /// @return 0: success, -1: failure. - int increase(); + int increase(); - /// Decrease (halve) the unit queue size. - /// @return 0: success, -1: failure. + /// Decrease (halve) the unit queue size. + /// @return 0: success, -1: failure. - int shrink(); + int shrink(); public: - int size() const { return m_iSize - m_iCount; } - int capacity() const { return m_iSize; } + int size() const { return m_iSize - m_iCount; } + int capacity() const { return m_iSize; } -public: // Operations on units +public: // Operations on units + /// find an available unit for incoming packet. + /// @return Pointer to the available unit, NULL if not found. - /// find an available unit for incoming packet. - /// @return Pointer to the available unit, NULL if not found. + CUnit* getNextAvailUnit(); - CUnit* getNextAvailUnit(); + void makeUnitFree(CUnit* unit); - void makeUnitFree(CUnit * unit); - - void makeUnitGood(CUnit * unit); + void makeUnitGood(CUnit* unit); public: - inline int getIPversion() const { return m_iIPversion; } + inline int getIPversion() const { return m_iIPversion; } private: - struct CQEntry - { - CUnit* m_pUnit; // unit queue - char* m_pBuffer; // data buffer - int m_iSize; // size of each queue + struct CQEntry + { + CUnit* m_pUnit; // unit queue + char* m_pBuffer; // data buffer + int m_iSize; // size of each queue - CQEntry* m_pNext; - } - *m_pQEntry, // pointer to the first unit queue - *m_pCurrQueue, // pointer to the current available queue - *m_pLastQueue; // pointer to the last unit queue + CQEntry* m_pNext; + } * m_pQEntry, // pointer to the first unit queue + *m_pCurrQueue, // pointer to the current available queue + *m_pLastQueue; // pointer to the last unit queue - CUnit* m_pAvailUnit; // recent available unit + CUnit* m_pAvailUnit; // recent available unit - int m_iSize; // total size of the unit queue, in number of packets - int m_iCount; // total number of valid (occupied) packets in the queue + int m_iSize; // total size of the unit queue, in number of packets + int m_iCount; // total number of valid (occupied) packets in the queue - int m_iMSS; // unit buffer size - int m_iIPversion; // IP version + int m_iMSS; // unit buffer size + int m_iIPversion; // IP version private: - CUnitQueue(const CUnitQueue&); - CUnitQueue& operator=(const CUnitQueue&); + CUnitQueue(const CUnitQueue&); + CUnitQueue& operator=(const CUnitQueue&); }; struct CSNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + srt::sync::steady_clock::time_point m_tsTimeStamp; - int m_iHeapLoc; // location on the heap, -1 means not on the heap + int m_iHeapLoc; // location on the heap, -1 means not on the heap }; class CSndUList { -friend class CSndQueue; + friend class CSndQueue; public: - CSndUList(); - ~CSndUList(); + CSndUList(); + ~CSndUList(); public: + enum EReschedule + { + DONT_RESCHEDULE = 0, + DO_RESCHEDULE = 1 + }; - enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 }; - - static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; } + static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; } - /// Update the timestamp of the UDT instance on the list. - /// @param [in] u pointer to the UDT instance - /// @param [in] reschedule if the timestamp should be rescheduled + /// Update the timestamp of the UDT instance on the list. + /// @param [in] u pointer to the UDT instance + /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); + void update(const CUDT* u, EReschedule reschedule); - /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. - /// @param [out] addr destination address of the next packet - /// @param [out] pkt the next packet to be sent - /// @return 1 if successfully retrieved, -1 if no packet found. + /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. + /// @param [out] addr destination address of the next packet + /// @param [out] pkt the next packet to be sent + /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); + int pop(sockaddr_any& addr, CPacket& pkt); - /// Remove UDT instance from the list. - /// @param [in] u pointer to the UDT instance + /// Remove UDT instance from the list. + /// @param [in] u pointer to the UDT instance - void remove(const CUDT* u); + void remove(const CUDT* u); - /// Retrieve the next scheduled processing time. - /// @return Scheduled processing time of the first UDT socket in the list. + /// Retrieve the next scheduled processing time. + /// @return Scheduled processing time of the first UDT socket in the list. - srt::sync::steady_clock::time_point getNextProcTime(); + srt::sync::steady_clock::time_point getNextProcTime(); private: + /// Doubles the size of the list. + /// + void realloc_(); - /// Doubles the size of the list. - /// - void realloc_(); + /// Insert a new UDT instance into the list with realloc if required. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); - /// Insert a new UDT instance into the list with realloc if required. - /// - /// @param [in] ts time stamp: next processing time - /// @param [in] u pointer to the UDT instance - void insert_(const srt::sync::steady_clock::time_point &ts, const CUDT* u); + /// Insert a new UDT instance into the list without realloc. + /// Should be called if there is a gauranteed space for the element. + /// + /// @param [in] ts time stamp: next processing time + /// @param [in] u pointer to the UDT instance + void insert_norealloc_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); - /// Insert a new UDT instance into the list without realloc. - /// Should be called if there is a gauranteed space for the element. - /// - /// @param [in] ts time stamp: next processing time - /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const srt::sync::steady_clock::time_point &ts, const CUDT* u); - - void remove_(const CUDT* u); + void remove_(const CUDT* u); private: - CSNode** m_pHeap; // The heap array - int m_iArrayLength; // physical length of the array - int m_iLastEntry; // position of last entry on the heap array + CSNode** m_pHeap; // The heap array + int m_iArrayLength; // physical length of the array + int m_iLastEntry; // position of last entry on the heap array - srt::sync::Mutex m_ListLock; + srt::sync::Mutex m_ListLock; - srt::sync::Mutex* m_pWindowLock; - srt::sync::Condition* m_pWindowCond; + srt::sync::Mutex* m_pWindowLock; + srt::sync::Condition* m_pWindowCond; - srt::sync::CTimer* m_pTimer; + srt::sync::CTimer* m_pTimer; private: - CSndUList(const CSndUList&); - CSndUList& operator=(const CSndUList&); + CSndUList(const CSndUList&); + CSndUList& operator=(const CSndUList&); }; struct CRNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp - CRNode* m_pPrev; // previous link - CRNode* m_pNext; // next link + CRNode* m_pPrev; // previous link + CRNode* m_pNext; // next link - bool m_bOnList; // if the node is already on the list + bool m_bOnList; // if the node is already on the list }; class CRcvUList { public: - CRcvUList(); - ~CRcvUList(); + CRcvUList(); + ~CRcvUList(); public: + /// Insert a new UDT instance to the list. + /// @param [in] u pointer to the UDT instance - /// Insert a new UDT instance to the list. - /// @param [in] u pointer to the UDT instance - - void insert(const CUDT* u); + void insert(const CUDT* u); - /// Remove the UDT instance from the list. - /// @param [in] u pointer to the UDT instance + /// Remove the UDT instance from the list. + /// @param [in] u pointer to the UDT instance - void remove(const CUDT* u); + void remove(const CUDT* u); - /// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. - /// @param [in] u pointer to the UDT instance + /// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing. + /// @param [in] u pointer to the UDT instance - void update(const CUDT* u); + void update(const CUDT* u); public: - CRNode* m_pUList; // the head node + CRNode* m_pUList; // the head node private: - CRNode* m_pLast; // the last node + CRNode* m_pLast; // the last node private: - CRcvUList(const CRcvUList&); - CRcvUList& operator=(const CRcvUList&); + CRcvUList(const CRcvUList&); + CRcvUList& operator=(const CRcvUList&); }; class CHash { public: - CHash(); - ~CHash(); + CHash(); + ~CHash(); public: + /// Initialize the hash table. + /// @param [in] size hash table size - /// Initialize the hash table. - /// @param [in] size hash table size - - void init(int size); + void init(int size); - /// Look for a UDT instance from the hash table. - /// @param [in] id socket ID - /// @return Pointer to a UDT instance, or NULL if not found. + /// Look for a UDT instance from the hash table. + /// @param [in] id socket ID + /// @return Pointer to a UDT instance, or NULL if not found. - CUDT* lookup(int32_t id); + CUDT* lookup(int32_t id); - /// Insert an entry to the hash table. - /// @param [in] id socket ID - /// @param [in] u pointer to the UDT instance + /// Insert an entry to the hash table. + /// @param [in] id socket ID + /// @param [in] u pointer to the UDT instance - void insert(int32_t id, CUDT* u); + void insert(int32_t id, CUDT* u); - /// Remove an entry from the hash table. - /// @param [in] id socket ID + /// Remove an entry from the hash table. + /// @param [in] id socket ID - void remove(int32_t id); + void remove(int32_t id); private: - struct CBucket - { - int32_t m_iID; // Socket ID - CUDT* m_pUDT; // Socket instance + struct CBucket + { + int32_t m_iID; // Socket ID + CUDT* m_pUDT; // Socket instance - CBucket* m_pNext; // next bucket - } **m_pBucket; // list of buckets (the hash table) + CBucket* m_pNext; // next bucket + } * *m_pBucket; // list of buckets (the hash table) - int m_iHashSize; // size of hash table + int m_iHashSize; // size of hash table private: - CHash(const CHash&); - CHash& operator=(const CHash&); + CHash(const CHash&); + CHash& operator=(const CHash&); }; /// @brief A queue of sockets pending for connection. @@ -336,8 +337,7 @@ class CRendezvousQueue /// @param u pointer to a corresponding CUDT instance. /// @param addr remote address to connect to. /// @param ttl timepoint for connection attempt to expire. - void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, - const srt::sync::steady_clock::time_point &ttl); + void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const srt::sync::steady_clock::time_point& ttl); /// @brief Remove a socket from the connection pending list. /// @param id socket ID. @@ -359,257 +359,256 @@ class CRendezvousQueue private: struct LinkStatusInfo { - CUDT* u; - SRTSOCKET id; - int errorcode; + CUDT* u; + SRTSOCKET id; + int errorcode; sockaddr_any peeraddr; - int token; + int token; struct HasID { SRTSOCKET id; - HasID(SRTSOCKET p) : id(p) {} - bool operator()(const LinkStatusInfo& i) + HasID(SRTSOCKET p) + : id(p) { - return i.id == id; } + bool operator()(const LinkStatusInfo& i) { return i.id == id; } }; }; /// @brief Qualify pending connections: /// - Sockets with expired TTL go to the 'to_remove' list and removed from the queue straight away. /// - If HS request is to be resent (resend 250 ms if no response from the peer) go to the 'to_process' list. - /// + /// /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. /// @param iDstSockID destination socket ID of the received packet. /// @param[in,out] toRemove stores sockets with expired TTL. /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. - bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, int iDstSockID, - std::vector& toRemove, std::vector& toProcess); + bool qualifyToHandle(EReadStatus rst, + EConnectStatus cst, + int iDstSockID, + std::vector& toRemove, + std::vector& toProcess); private: - struct CRL - { - SRTSOCKET m_iID; // SRT socket ID (self) - CUDT* m_pUDT; // CUDT instance - sockaddr_any m_PeerAddr;// SRT sonnection peer address - srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires - }; - std::list m_lRendezvousID; // The sockets currently in rendezvous mode - - mutable srt::sync::Mutex m_RIDListLock; + struct CRL + { + SRTSOCKET m_iID; // SRT socket ID (self) + CUDT* m_pUDT; // CUDT instance + sockaddr_any m_PeerAddr; // SRT sonnection peer address + srt::sync::steady_clock::time_point m_tsTTL; // the time that this request expires + }; + std::list m_lRendezvousID; // The sockets currently in rendezvous mode + + mutable srt::sync::Mutex m_RIDListLock; }; class CSndQueue { -friend class CUDT; -friend class CUDTUnited; + friend class CUDT; + friend class CUDTUnited; public: - CSndQueue(); - ~CSndQueue(); + CSndQueue(); + ~CSndQueue(); public: + // XXX There's currently no way to access the socket ID set for + // whatever the queue is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the queue is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - /// Initialize the sending queue. - /// @param [in] c UDP channel to be associated to the queue - /// @param [in] t Timer + /// Initialize the sending queue. + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t Timer - void init(CChannel* c, srt::sync::CTimer* t); + void init(CChannel* c, srt::sync::CTimer* t); - /// Send out a packet to a given address. - /// @param [in] addr destination address - /// @param [in] packet packet to be sent out - /// @return Size of data sent out. + /// Send out a packet to a given address. + /// @param [in] addr destination address + /// @param [in] packet packet to be sent out + /// @return Size of data sent out. - int sendto(const sockaddr_any& addr, CPacket& packet); + int sendto(const sockaddr_any& addr, CPacket& packet); - /// Get the IP TTL. - /// @param [in] ttl IP Time To Live. - /// @return TTL. + /// Get the IP TTL. + /// @param [in] ttl IP Time To Live. + /// @return TTL. - int getIpTTL() const; + int getIpTTL() const; - /// Get the IP Type of Service. - /// @return ToS. + /// Get the IP Type of Service. + /// @return ToS. - int getIpToS() const; + int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE - bool getBind(char* dst, size_t len) const; + bool getBind(char* dst, size_t len) const; #endif - int ioctlQuery(int type) const; - int sockoptQuery(int level, int type) const; + int ioctlQuery(int type) const; + int sockoptQuery(int level, int type) const; - void setClosing() - { - m_bClosing = true; - } + void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; + static void* worker(void* param); + srt::sync::CThread m_WorkerThread; private: - CSndUList* m_pSndUList; // List of UDT instances for data sending - CChannel* m_pChannel; // The UDP channel for data sending - srt::sync::CTimer* m_pTimer; // Timing facility + CSndUList* m_pSndUList; // List of UDT instances for data sending + CChannel* m_pChannel; // The UDP channel for data sending + srt::sync::CTimer* m_pTimer; // Timing facility - srt::sync::Mutex m_WindowLock; - srt::sync::Condition m_WindowCond; + srt::sync::Mutex m_WindowLock; + srt::sync::Condition m_WindowCond; - volatile bool m_bClosing; // closing the worker + volatile bool m_bClosing; // closing the worker -#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker - uint64_t m_ullDbgPeriod; - uint64_t m_ullDbgTime; - struct { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker + uint64_t m_ullDbgPeriod; + uint64_t m_ullDbgTime; + struct + { unsigned long lIteration; // - unsigned long lSleepTo; //SleepTo - unsigned long lNotReadyPop; //Continue + unsigned long lSleepTo; // SleepTo + unsigned long lNotReadyPop; // Continue unsigned long lSendTo; - unsigned long lNotReadyTs; - unsigned long lCondWait; //block on m_WindowCond - } m_WorkerStats; + unsigned long lNotReadyTs; + unsigned long lCondWait; // block on m_WindowCond + } m_WorkerStats; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ #if ENABLE_LOGGING - static int m_counter; + static int m_counter; #endif private: - CSndQueue(const CSndQueue&); - CSndQueue& operator=(const CSndQueue&); + CSndQueue(const CSndQueue&); + CSndQueue& operator=(const CSndQueue&); }; class CRcvQueue { -friend class CUDT; -friend class CUDTUnited; + friend class CUDT; + friend class CUDTUnited; public: - CRcvQueue(); - ~CRcvQueue(); + CRcvQueue(); + ~CRcvQueue(); public: + // XXX There's currently no way to access the socket ID set for + // whatever the queue is currently working. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - // XXX There's currently no way to access the socket ID set for - // whatever the queue is currently working. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } - - /// Initialize the receiving queue. - /// @param [in] size queue size - /// @param [in] mss maximum packet size - /// @param [in] version IP version - /// @param [in] hsize hash table size - /// @param [in] c UDP channel to be associated to the queue - /// @param [in] t timer + /// Initialize the receiving queue. + /// @param [in] size queue size + /// @param [in] mss maximum packet size + /// @param [in] version IP version + /// @param [in] hsize hash table size + /// @param [in] c UDP channel to be associated to the queue + /// @param [in] t timer - void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); + void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); - /// Read a packet for a specific UDT socket id. - /// @param [in] id Socket ID - /// @param [out] packet received packet - /// @return Data size of the packet + /// Read a packet for a specific UDT socket id. + /// @param [in] id Socket ID + /// @param [out] packet received packet + /// @return Data size of the packet - int recvfrom(int32_t id, CPacket& to_packet); + int recvfrom(int32_t id, CPacket& to_packet); - void stopWorker(); + void stopWorker(); - void setClosing() - { - m_bClosing = true; - } + void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; - // Subroutines of worker - EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); - EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); + static void* worker(void* param); + srt::sync::CThread m_WorkerThread; + // Subroutines of worker + EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); + EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); private: - CUnitQueue m_UnitQueue; // The received packet queue - CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue - CHash* m_pHash; // Hash table for UDT socket looking up - CChannel* m_pChannel; // UDP channel for receving packets - srt::sync::CTimer* m_pTimer; // shared timer with the snd queue + CUnitQueue m_UnitQueue; // The received packet queue + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + srt::sync::CTimer* m_pTimer; // shared timer with the snd queue - size_t m_szPayloadSize; // packet payload size + size_t m_szPayloadSize; // packet payload size - volatile bool m_bClosing; // closing the worker + volatile bool m_bClosing; // closing the worker #if ENABLE_LOGGING - static int m_counter; + static int m_counter; #endif private: - int setListener(CUDT* u); - void removeListener(const CUDT* u); + int setListener(CUDT* u); + void removeListener(const CUDT* u); - void registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const srt::sync::steady_clock::time_point& ttl); - void removeConnector(const SRTSOCKET& id); + void registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const srt::sync::steady_clock::time_point& ttl); + void removeConnector(const SRTSOCKET& id); - void setNewEntry(CUDT* u); - bool ifNewEntry(); - CUDT* getNewEntry(); + void setNewEntry(CUDT* u); + bool ifNewEntry(); + CUDT* getNewEntry(); - void storePkt(int32_t id, CPacket* pkt); + void storePkt(int32_t id, CPacket* pkt); private: - srt::sync::Mutex m_LSLock; - CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity - CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode + srt::sync::Mutex m_LSLock; + CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity + CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode - std::vector m_vNewEntry; // newly added entries, to be inserted - srt::sync::Mutex m_IDLock; + std::vector m_vNewEntry; // newly added entries, to be inserted + srt::sync::Mutex m_IDLock; - std::map > m_mBuffer; // temporary buffer for rendezvous connection request - srt::sync::Mutex m_BufferLock; - srt::sync::Condition m_BufferCond; + std::map > m_mBuffer; // temporary buffer for rendezvous connection request + srt::sync::Mutex m_BufferLock; + srt::sync::Condition m_BufferCond; private: - CRcvQueue(const CRcvQueue&); - CRcvQueue& operator=(const CRcvQueue&); + CRcvQueue(const CRcvQueue&); + CRcvQueue& operator=(const CRcvQueue&); }; struct CMultiplexer { - CSndQueue* m_pSndQueue; // The sending queue - CRcvQueue* m_pRcvQueue; // The receiving queue - CChannel* m_pChannel; // The UDP channel for sending and receiving - srt::sync::CTimer* m_pTimer; // The timer - - int m_iPort; // The UDP port number of this multiplexer - int m_iIPversion; // Address family (AF_INET or AF_INET6) - int m_iRefCount; // number of UDT instances that are associated with this multiplexer - - CSrtMuxerConfig m_mcfg; - - int m_iID; // multiplexer ID - - // Constructor should reset all pointers to NULL - // to prevent dangling pointer when checking for memory alloc fails - CMultiplexer() - : m_pSndQueue(NULL) - , m_pRcvQueue(NULL) - , m_pChannel(NULL) - , m_pTimer(NULL) + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + srt::sync::CTimer* m_pTimer; // The timer + + int m_iPort; // The UDP port number of this multiplexer + int m_iIPversion; // Address family (AF_INET or AF_INET6) + int m_iRefCount; // number of UDT instances that are associated with this multiplexer + + CSrtMuxerConfig m_mcfg; + + int m_iID; // multiplexer ID + + // Constructor should reset all pointers to NULL + // to prevent dangling pointer when checking for memory alloc fails + CMultiplexer() + : m_pSndQueue(NULL) + , m_pRcvQueue(NULL) + , m_pChannel(NULL) + , m_pTimer(NULL) { } - void destroy(); + void destroy(); }; #endif From 445a60c19cbda6dbf24087910f5b868d90a6bddc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 12:14:03 +0200 Subject: [PATCH 081/683] [core] Refax: placing SRT classes inside 'srt' namespace. CUDT, CUDTUnited, CUDTSocket, CUDTGroup, etc. --- srtcore/api.cpp | 370 ++++++++++++++++------------------- srtcore/api.h | 38 ++-- srtcore/buffer.cpp | 15 +- srtcore/buffer.h | 16 +- srtcore/channel.cpp | 43 ++-- srtcore/channel.h | 7 +- srtcore/common.cpp | 1 + srtcore/common.h | 12 +- srtcore/congctl.cpp | 1 + srtcore/congctl.h | 18 +- srtcore/core.cpp | 238 +++++++++++----------- srtcore/core.h | 27 +-- srtcore/crypto.cpp | 1 + srtcore/crypto.h | 14 +- srtcore/epoll.h | 13 +- srtcore/fec.cpp | 13 +- srtcore/fec.h | 4 + srtcore/group.cpp | 4 + srtcore/group.h | 62 +++--- srtcore/handshake.cpp | 25 +-- srtcore/packet.cpp | 68 ++++--- srtcore/packet.h | 8 +- srtcore/packetfilter.cpp | 44 +++-- srtcore/packetfilter.h | 4 + srtcore/packetfilter_api.h | 4 +- srtcore/queue.cpp | 134 ++++++------- srtcore/queue.h | 72 +++---- srtcore/socketconfig.h | 16 +- srtcore/srt_c_api.cpp | 1 + srtcore/udt.h | 2 +- srtcore/window.cpp | 4 +- srtcore/window.h | 8 +- test/test_buffer.cpp | 2 + test/test_fec_rebuilding.cpp | 21 +- test/test_seqno.cpp | 2 + test/test_unitqueue.cpp | 3 +- testing/testmedia.cpp | 2 +- 37 files changed, 686 insertions(+), 631 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 542832f77..44a068764 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -83,7 +83,7 @@ using namespace srt::sync; extern LogConfig srt_logger_config; -void CUDTSocket::construct() +void srt::CUDTSocket::construct() { #if ENABLE_EXPERIMENTAL_BONDING m_GroupOf = NULL; @@ -94,7 +94,7 @@ void CUDTSocket::construct() setupMutex(m_ControlLock, "Control"); } -CUDTSocket::~CUDTSocket() +srt::CUDTSocket::~CUDTSocket() { delete m_pUDT; @@ -106,7 +106,7 @@ CUDTSocket::~CUDTSocket() } -SRT_SOCKSTATUS CUDTSocket::getStatus() +SRT_SOCKSTATUS srt::CUDTSocket::getStatus() { // TTL in CRendezvousQueue::updateConnStatus() will set m_bConnecting to false. // Although m_Status is still SRTS_CONNECTING, the connection is in fact to be closed due to TTL expiry. @@ -124,7 +124,7 @@ SRT_SOCKSTATUS CUDTSocket::getStatus() } // [[using locked(m_GlobControlLock)]] -void CUDTSocket::breakSocket_LOCKED() +void srt::CUDTSocket::breakSocket_LOCKED() { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. @@ -135,7 +135,7 @@ void CUDTSocket::breakSocket_LOCKED() setClosed(); } -void CUDTSocket::setClosed() +void srt::CUDTSocket::setClosed() { m_Status = SRTS_CLOSED; @@ -146,14 +146,14 @@ void CUDTSocket::setClosed() m_tsClosureTimeStamp = steady_clock::now(); } -void CUDTSocket::setBrokenClosed() +void srt::CUDTSocket::setBrokenClosed() { m_pUDT->m_iBrokenCounter = 60; m_pUDT->m_bBroken = true; setClosed(); } -bool CUDTSocket::readReady() +bool srt::CUDTSocket::readReady() { if (m_pUDT->m_bConnected && m_pUDT->m_pRcvBuffer->isRcvDataReady()) return true; @@ -165,33 +165,33 @@ bool CUDTSocket::readReady() return broken(); } -bool CUDTSocket::writeReady() const +bool srt::CUDTSocket::writeReady() const { return (m_pUDT->m_bConnected && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) || broken(); } -bool CUDTSocket::broken() const +bool srt::CUDTSocket::broken() const { return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; } //////////////////////////////////////////////////////////////////////////////// -CUDTUnited::CUDTUnited(): -m_Sockets(), -m_GlobControlLock(), -m_IDLock(), -m_mMultiplexer(), -m_MultiplexerLock(), -m_pCache(NULL), -m_bClosing(false), -m_GCStopCond(), -m_InitLock(), -m_iInstanceCount(0), -m_bGCStatus(false), -m_ClosedSockets() +srt::CUDTUnited::CUDTUnited(): + m_Sockets(), + m_GlobControlLock(), + m_IDLock(), + m_mMultiplexer(), + m_MultiplexerLock(), + m_pCache(NULL), + m_bClosing(false), + m_GCStopCond(), + m_InitLock(), + m_iInstanceCount(0), + m_bGCStatus(false), + m_ClosedSockets() { // Socket ID MUST start from a random value m_SocketIDGenerator = genRandomInt(1, MAX_SOCKET_VAL); @@ -207,7 +207,7 @@ m_ClosedSockets() m_pCache = new CCache; } -CUDTUnited::~CUDTUnited() +srt::CUDTUnited::~CUDTUnited() { // Call it if it wasn't called already. // This will happen at the end of main() of the application, @@ -224,7 +224,7 @@ CUDTUnited::~CUDTUnited() delete m_pCache; } -std::string CUDTUnited::CONID(SRTSOCKET sock) +string srt::CUDTUnited::CONID(SRTSOCKET sock) { if ( sock == 0 ) return ""; @@ -234,7 +234,7 @@ std::string CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -int CUDTUnited::startup() +int srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); @@ -277,7 +277,7 @@ int CUDTUnited::startup() return 0; } -int CUDTUnited::cleanup() +int srt::CUDTUnited::cleanup() { // IMPORTANT!!! // In this function there must be NO LOGGING AT ALL. This function may @@ -324,7 +324,7 @@ int CUDTUnited::cleanup() return 0; } -SRTSOCKET CUDTUnited::generateSocketID(bool for_group) +SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) { ScopedLock guard(m_IDLock); @@ -426,7 +426,7 @@ SRTSOCKET CUDTUnited::generateSocketID(bool for_group) return sockval; } -SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) +SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) { // XXX consider using some replacement of std::unique_ptr // so that exceptions will clean up the object without the @@ -484,7 +484,7 @@ SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } -int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, +int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs, int& w_error, CUDT*& w_acpu) { CUDTSocket* ns = NULL; @@ -852,12 +852,12 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, } // static forwarder -int CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return s_UDTUnited.installAcceptHook(lsn, hook, opaq); } -int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { try { @@ -873,12 +873,12 @@ int CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* h return 0; } -int CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return s_UDTUnited.installConnectHook(lsn, hook, opaq); } -int CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) +int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) { try { @@ -902,7 +902,7 @@ int CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* h return 0; } -SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u) +SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) { // protects the m_Sockets structure ScopedLock cg(m_GlobControlLock); @@ -919,7 +919,7 @@ SRT_SOCKSTATUS CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } -int CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) +int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -937,7 +937,7 @@ int CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) return 0; } -int CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) +int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) { ScopedLock cg(s->m_ControlLock); @@ -966,7 +966,7 @@ int CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) return 0; } -int CUDTUnited::listen(const SRTSOCKET u, int backlog) +int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) { if (backlog <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -1012,7 +1012,7 @@ int CUDTUnited::listen(const SRTSOCKET u, int backlog) return 0; } -SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) +SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) { CEPollDesc* ed = 0; int eid = m_EPoll.create(&ed); @@ -1056,7 +1056,7 @@ SRTSOCKET CUDTUnited::accept_bond(const SRTSOCKET listeners [], int lsize, int64 return accept(lsn, ((sockaddr*)&dummy), (&outlen)); } -SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) +SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_addrlen) { if (pw_addr && !pw_addrlen) { @@ -1190,7 +1190,7 @@ SRTSOCKET CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int* pw_ return u; } -int CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) +int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified if (!srcname || !tarname || size_t(namelen) < sizeof (sockaddr_in)) @@ -1235,7 +1235,7 @@ int CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* ta return connectIn(s, target_addr, SRT_SEQNO_NONE); } -int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { sockaddr_any target_addr(name, namelen); if (target_addr.len == 0) @@ -1266,7 +1266,7 @@ int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, in } #if ENABLE_EXPERIMENTAL_BONDING -int CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) +int srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) { int gstat = groupConnect(pg, gd, 1); if (gstat == -1) @@ -1286,7 +1286,7 @@ int CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) } // [[using assert(pg->m_iBusy > 0)]] -int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) +int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) { CUDTGroup& g = *pg; SRT_ASSERT(g.m_iBusy > 0); @@ -1822,7 +1822,7 @@ int CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int ar #endif -int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) +int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) { ScopedLock cg(s->m_ControlLock); // a socket can "connect" only if it is in the following states: @@ -1892,7 +1892,7 @@ int CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_ } -int CUDTUnited::close(const SRTSOCKET u) +int srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) @@ -1911,7 +1911,7 @@ int CUDTUnited::close(const SRTSOCKET u) } #if ENABLE_EXPERIMENTAL_BONDING -void CUDTUnited::deleteGroup(CUDTGroup* g) +void srt::CUDTUnited::deleteGroup(CUDTGroup* g) { using srt_logging::gmlog; @@ -1920,7 +1920,7 @@ void CUDTUnited::deleteGroup(CUDTGroup* g) } // [[using locked(m_GlobControlLock)]] -void CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) +void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) { SRT_ASSERT(g->groupEmpty()); @@ -1960,7 +1960,7 @@ void CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int CUDTUnited::close(CUDTSocket* s) +int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); @@ -2118,7 +2118,7 @@ int CUDTUnited::close(CUDTSocket* s) return 0; } -void CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) +void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) { if (!pw_name || !pw_namelen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -2142,7 +2142,7 @@ void CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namel *pw_namelen = len; } -void CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) +void srt::CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) { if (!pw_name || !pw_namelen) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -2166,7 +2166,7 @@ void CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_namel *pw_namelen = len; } -int CUDTUnited::select( +int srt::CUDTUnited::select( UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout) { const steady_clock::time_point entertime = steady_clock::now(); @@ -2276,7 +2276,7 @@ int CUDTUnited::select( return count; } -int CUDTUnited::selectEx( +int srt::CUDTUnited::selectEx( const vector& fds, vector* readfds, vector* writefds, @@ -2350,17 +2350,17 @@ int CUDTUnited::selectEx( return count; } -int CUDTUnited::epoll_create() +int srt::CUDTUnited::epoll_create() { return m_EPoll.create(); } -int CUDTUnited::epoll_clear_usocks(int eid) +int srt::CUDTUnited::epoll_clear_usocks(int eid) { return m_EPoll.clear_usocks(eid); } -int CUDTUnited::epoll_add_usock( +int srt::CUDTUnited::epoll_add_usock( const int eid, const SRTSOCKET u, const int* events) { int ret = -1; @@ -2390,27 +2390,27 @@ int CUDTUnited::epoll_add_usock( // NOTE: WILL LOCK (serially): // - CEPoll::m_EPollLock // - CUDT::m_RecvLock -int CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) +int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) { int ret = m_EPoll.update_usock(eid, s->m_SocketID, events); s->m_pUDT->addEPoll(eid); return ret; } -int CUDTUnited::epoll_add_ssock( +int srt::CUDTUnited::epoll_add_ssock( const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } -int CUDTUnited::epoll_update_ssock( +int srt::CUDTUnited::epoll_update_ssock( const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.update_ssock(eid, s, events); } template -int CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) +int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) { // XXX Not sure if this is anyhow necessary because setting readiness // to false doesn't actually trigger any action. Further research needed. @@ -2432,19 +2432,19 @@ int CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) } // Needed internal access! -int CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) +int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { return epoll_remove_entity(eid, s->m_pUDT); } #if ENABLE_EXPERIMENTAL_BONDING -int CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) +int srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) { return epoll_remove_entity(eid, g); } #endif -int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { CUDTSocket* s = 0; @@ -2470,12 +2470,12 @@ int CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) return m_EPoll.update_usock(eid, u, &no_events); } -int CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +int srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } -int CUDTUnited::epoll_uwait( +int srt::CUDTUnited::epoll_uwait( const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, @@ -2484,17 +2484,17 @@ int CUDTUnited::epoll_uwait( return m_EPoll.uwait(eid, fdsSet, fdsSize, msTimeOut); } -int32_t CUDTUnited::epoll_set(int eid, int32_t flags) +int32_t srt::CUDTUnited::epoll_set(int eid, int32_t flags) { return m_EPoll.setflags(eid, flags); } -int CUDTUnited::epoll_release(const int eid) +int srt::CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } -CUDTSocket* CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) +srt::CUDTSocket* srt::CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) { ScopedLock cg (m_GlobControlLock); CUDTSocket* s = locateSocket_LOCKED(u); @@ -2509,7 +2509,7 @@ CUDTSocket* CUDTUnited::locateSocket(const SRTSOCKET u, ErrorHandling erh) } // [[using locked(m_GlobControlLock)]]; -CUDTSocket* CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) +srt::CUDTSocket* srt::CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) { sockets_t::iterator i = m_Sockets.find(u); @@ -2522,7 +2522,7 @@ CUDTSocket* CUDTUnited::locateSocket_LOCKED(SRTSOCKET u) } #if ENABLE_EXPERIMENTAL_BONDING -CUDTGroup* CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) +srt::CUDTGroup* srt::CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) { ScopedLock cg (m_GlobControlLock); @@ -2539,7 +2539,7 @@ CUDTGroup* CUDTUnited::locateAcquireGroup(SRTSOCKET u, ErrorHandling erh) return i->second; } -CUDTGroup* CUDTUnited::acquireSocketsGroup(CUDTSocket* s) +srt::CUDTGroup* srt::CUDTUnited::acquireSocketsGroup(CUDTSocket* s) { ScopedLock cg (m_GlobControlLock); CUDTGroup* g = s->m_GroupOf; @@ -2553,7 +2553,7 @@ CUDTGroup* CUDTUnited::acquireSocketsGroup(CUDTSocket* s) } #endif -CUDTSocket* CUDTUnited::locatePeer( +srt::CUDTSocket* srt::CUDTUnited::locatePeer( const sockaddr_any& peer, const SRTSOCKET id, int32_t isn) @@ -2582,7 +2582,7 @@ CUDTSocket* CUDTUnited::locatePeer( return NULL; } -void CUDTUnited::checkBrokenSockets() +void srt::CUDTUnited::checkBrokenSockets() { ScopedLock cg(m_GlobControlLock); @@ -2724,7 +2724,7 @@ void CUDTUnited::checkBrokenSockets() } // [[using locked(m_GlobControlLock)]] -void CUDTUnited::removeSocket(const SRTSOCKET u) +void srt::CUDTUnited::removeSocket(const SRTSOCKET u) { sockets_t::iterator i = m_ClosedSockets.find(u); @@ -2829,7 +2829,7 @@ void CUDTUnited::removeSocket(const SRTSOCKET u) } } -void CUDTUnited::updateMux( +void srt::CUDTUnited::updateMux( CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) { ScopedLock cg(m_GlobControlLock); @@ -2943,7 +2943,7 @@ void CUDTUnited::updateMux( // exists, otherwise the dispatching procedure wouldn't even call this // function. By historical reasons there's also a fallback for a case when the // multiplexer wasn't found by id, the search by port number continues. -bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) +bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) { ScopedLock cg(m_GlobControlLock); const int port = ls->m_SelfAddr.hport(); @@ -3026,7 +3026,7 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) return false; } -void* CUDTUnited::garbageCollect(void* p) +void* srt::CUDTUnited::garbageCollect(void* p) { CUDTUnited* self = (CUDTUnited*)p; @@ -3108,17 +3108,17 @@ void* CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -int CUDT::startup() +int srt::CUDT::startup() { return s_UDTUnited.startup(); } -int CUDT::cleanup() +int srt::CUDT::cleanup() { return s_UDTUnited.cleanup(); } -SRTSOCKET CUDT::socket() +SRTSOCKET srt::CUDT::socket() { if (!s_UDTUnited.m_bGCStatus) s_UDTUnited.startup(); @@ -3147,12 +3147,12 @@ SRTSOCKET CUDT::socket() } } -CUDT::APIError::APIError(const CUDTException& e) +srt::CUDT::APIError::APIError(const CUDTException& e) { SetThreadLocalError(e); } -CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) +srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) { SetThreadLocalError(CUDTException(mj, mn, syserr)); } @@ -3162,7 +3162,7 @@ CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) // This doesn't have argument of GroupType due to header file conflicts. // [[using locked(s_UDTUnited.m_GlobControlLock)]] -CUDTGroup& CUDT::newGroup(const int type) +srt::CUDTGroup& srt::CUDT::newGroup(const int type) { const SRTSOCKET id = s_UDTUnited.generateSocketID(true); @@ -3170,7 +3170,7 @@ CUDTGroup& CUDT::newGroup(const int type) return s_UDTUnited.addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); } -SRTSOCKET CUDT::createGroup(SRT_GROUP_TYPE gt) +SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() if (!s_UDTUnited.m_bGCStatus) @@ -3199,7 +3199,7 @@ SRTSOCKET CUDT::createGroup(SRT_GROUP_TYPE gt) } -int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) +int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) { // Check if socket and group have been set correctly. int32_t sid = socket & ~SRTGROUP_MASK; @@ -3253,7 +3253,7 @@ int CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) // dead function as for now. This is only for non-managed // groups. -int CUDT::removeSocketFromGroup(SRTSOCKET socket) +int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) { CUDTSocket* s = s_UDTUnited.locateSocket(socket); if (!s) @@ -3270,7 +3270,7 @@ int CUDT::removeSocketFromGroup(SRTSOCKET socket) // [[using locked(m_ControlLock)]] // [[using locked(CUDT::s_UDTUnited.m_GlobControlLock)]] -void CUDTSocket::removeFromGroup(bool broken) +void srt::CUDTSocket::removeFromGroup(bool broken) { CUDTGroup* g = m_GroupOf; if (g) @@ -3299,7 +3299,7 @@ void CUDTSocket::removeFromGroup(bool broken) } } -SRTSOCKET CUDT::getGroupOfSocket(SRTSOCKET socket) +SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) { // Lock this for the whole function as we need the group // to persist the call. @@ -3311,7 +3311,7 @@ SRTSOCKET CUDT::getGroupOfSocket(SRTSOCKET socket) return s->m_GroupOf->id(); } -int CUDT::configureGroup(SRTSOCKET groupid, const char* str) +int srt::CUDT::configureGroup(SRTSOCKET groupid, const char* str) { if ( (groupid & SRTGROUP_MASK) == 0) { @@ -3327,7 +3327,7 @@ int CUDT::configureGroup(SRTSOCKET groupid, const char* str) return k.group->configure(str); } -int CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) +int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { if ((groupid & SRTGROUP_MASK) == 0 || !psize) { @@ -3345,7 +3345,7 @@ int CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psiz } #endif -int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) { try { @@ -3381,7 +3381,7 @@ int CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) } } -int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { @@ -3407,7 +3407,7 @@ int CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) } } -int CUDT::listen(SRTSOCKET u, int backlog) +int srt::CUDT::listen(SRTSOCKET u, int backlog) { try { @@ -3429,7 +3429,7 @@ int CUDT::listen(SRTSOCKET u, int backlog) } } -SRTSOCKET CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) +SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut) { try { @@ -3454,7 +3454,7 @@ SRTSOCKET CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msT } } -SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) +SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) { try { @@ -3479,7 +3479,7 @@ SRTSOCKET CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) } } -int CUDT::connect( +int srt::CUDT::connect( SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) { try @@ -3503,7 +3503,7 @@ int CUDT::connect( } #if ENABLE_EXPERIMENTAL_BONDING -int CUDT::connectLinks(SRTSOCKET grp, +int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets [], int arraysize) { if (arraysize <= 0) @@ -3537,7 +3537,7 @@ int CUDT::connectLinks(SRTSOCKET grp, } #endif -int CUDT::connect( +int srt::CUDT::connect( SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { try @@ -3560,7 +3560,7 @@ int CUDT::connect( } } -int CUDT::close(SRTSOCKET u) +int srt::CUDT::close(SRTSOCKET u) { try { @@ -3578,7 +3578,7 @@ int CUDT::close(SRTSOCKET u) } } -int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { @@ -3597,7 +3597,7 @@ int CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) } } -int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { @@ -3616,7 +3616,7 @@ int CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } -int CUDT::getsockopt( +int srt::CUDT::getsockopt( SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) @@ -3651,7 +3651,7 @@ int CUDT::getsockopt( } } -int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { if (!optval) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3683,7 +3683,7 @@ int CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, } } -int CUDT::send(SRTSOCKET u, const char* buf, int len, int) +int srt::CUDT::send(SRTSOCKET u, const char* buf, int len, int) { SRT_MSGCTRL mctrl = srt_msgctrl_default; return sendmsg2(u, buf, len, (mctrl)); @@ -3691,7 +3691,7 @@ int CUDT::send(SRTSOCKET u, const char* buf, int len, int) // --> CUDT::recv moved down -int CUDT::sendmsg( +int srt::CUDT::sendmsg( SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, int64_t srctime) { @@ -3702,7 +3702,7 @@ int CUDT::sendmsg( return sendmsg2(u, buf, len, (mctrl)); } -int CUDT::sendmsg2( +int srt::CUDT::sendmsg2( SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) { try @@ -3733,14 +3733,14 @@ int CUDT::sendmsg2( } } -int CUDT::recv(SRTSOCKET u, char* buf, int len, int) +int srt::CUDT::recv(SRTSOCKET u, char* buf, int len, int) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int ret = recvmsg2(u, buf, len, (mctrl)); return ret; } -int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) +int srt::CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int ret = recvmsg2(u, buf, len, (mctrl)); @@ -3748,7 +3748,7 @@ int CUDT::recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) return ret; } -int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) +int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) { try { @@ -3774,7 +3774,7 @@ int CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) } } -int64_t CUDT::sendfile( +int64_t srt::CUDT::sendfile( SRTSOCKET u, fstream& ifs, int64_t& offset, int64_t size, int block) { try @@ -3798,7 +3798,7 @@ int64_t CUDT::sendfile( } } -int64_t CUDT::recvfile( +int64_t srt::CUDT::recvfile( SRTSOCKET u, fstream& ofs, int64_t& offset, int64_t size, int block) { try @@ -3817,7 +3817,7 @@ int64_t CUDT::recvfile( } } -int CUDT::select( +int srt::CUDT::select( int, UDT::UDSET* readfds, UDT::UDSET* writefds, @@ -3849,7 +3849,7 @@ int CUDT::select( } } -int CUDT::selectEx( +int srt::CUDT::selectEx( const vector& fds, vector* readfds, vector* writefds, @@ -3881,7 +3881,7 @@ int CUDT::selectEx( } } -int CUDT::epoll_create() +int srt::CUDT::epoll_create() { try { @@ -3899,7 +3899,7 @@ int CUDT::epoll_create() } } -int CUDT::epoll_clear_usocks(int eid) +int srt::CUDT::epoll_clear_usocks(int eid) { try { @@ -3917,7 +3917,7 @@ int CUDT::epoll_clear_usocks(int eid) } } -int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { try { @@ -3935,7 +3935,7 @@ int CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) } } -int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { @@ -3953,7 +3953,7 @@ int CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) } } -int CUDT::epoll_update_usock( +int srt::CUDT::epoll_update_usock( const int eid, const SRTSOCKET u, const int* events) { try @@ -3972,7 +3972,7 @@ int CUDT::epoll_update_usock( } } -int CUDT::epoll_update_ssock( +int srt::CUDT::epoll_update_ssock( const int eid, const SYSSOCKET s, const int* events) { try @@ -3992,7 +3992,7 @@ int CUDT::epoll_update_ssock( } -int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { @@ -4010,7 +4010,7 @@ int CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) } } -int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { @@ -4028,7 +4028,7 @@ int CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) } } -int CUDT::epoll_wait( +int srt::CUDT::epoll_wait( const int eid, set* readfds, set* writefds, @@ -4053,7 +4053,7 @@ int CUDT::epoll_wait( } } -int CUDT::epoll_uwait( +int srt::CUDT::epoll_uwait( const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, @@ -4075,7 +4075,7 @@ int CUDT::epoll_uwait( } } -int32_t CUDT::epoll_set( +int32_t srt::CUDT::epoll_set( const int eid, int32_t flags) { @@ -4095,7 +4095,7 @@ int32_t CUDT::epoll_set( } } -int CUDT::epoll_release(const int eid) +int srt::CUDT::epoll_release(const int eid) { try { @@ -4113,12 +4113,12 @@ int CUDT::epoll_release(const int eid) } } -CUDTException& CUDT::getlasterror() +CUDTException& srt::CUDT::getlasterror() { return GetThreadLocalError(); } -int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) @@ -4144,7 +4144,7 @@ int CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous } #if ENABLE_EXPERIMENTAL_BONDING -int CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) +int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { @@ -4167,7 +4167,7 @@ int CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) } #endif -CUDT* CUDT::getUDTHandle(SRTSOCKET u) +srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { @@ -4187,7 +4187,7 @@ CUDT* CUDT::getUDTHandle(SRTSOCKET u) } } -vector CUDT::existingSockets() +vector srt::CUDT::existingSockets() { vector out; for (CUDTUnited::sockets_t::iterator i = s_UDTUnited.m_Sockets.begin(); @@ -4198,7 +4198,7 @@ vector CUDT::existingSockets() return out; } -SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u) +SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) { try { @@ -4225,7 +4225,6 @@ SRT_SOCKSTATUS CUDT::getsockstate(SRTSOCKET u) } } - //////////////////////////////////////////////////////////////////////////////// namespace UDT @@ -4233,64 +4232,64 @@ namespace UDT int startup() { - return CUDT::startup(); + return srt::CUDT::startup(); } int cleanup() { - return CUDT::cleanup(); + return srt::CUDT::cleanup(); } int bind(SRTSOCKET u, const struct sockaddr* name, int namelen) { - return CUDT::bind(u, name, namelen); + return srt::CUDT::bind(u, name, namelen); } int bind2(SRTSOCKET u, UDPSOCKET udpsock) { - return CUDT::bind(u, udpsock); + return srt::CUDT::bind(u, udpsock); } int listen(SRTSOCKET u, int backlog) { - return CUDT::listen(u, backlog); + return srt::CUDT::listen(u, backlog); } SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen) { - return CUDT::accept(u, addr, addrlen); + return srt::CUDT::accept(u, addr, addrlen); } int connect(SRTSOCKET u, const struct sockaddr* name, int namelen) { - return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); + return srt::CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } int close(SRTSOCKET u) { - return CUDT::close(u); + return srt::CUDT::close(u); } int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) { - return CUDT::getpeername(u, name, namelen); + return srt::CUDT::getpeername(u, name, namelen); } int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen) { - return CUDT::getsockname(u, name, namelen); + return srt::CUDT::getsockname(u, name, namelen); } int getsockopt( SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen) { - return CUDT::getsockopt(u, level, optname, optval, optlen); + return srt::CUDT::getsockopt(u, level, optname, optval, optlen); } int setsockopt( SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen) { - return CUDT::setsockopt(u, level, optname, optval, optlen); + return srt::CUDT::setsockopt(u, level, optname, optval, optlen); } // DEVELOPER API @@ -4298,17 +4297,17 @@ int setsockopt( int connect_debug( SRTSOCKET u, const struct sockaddr* name, int namelen, int32_t forced_isn) { - return CUDT::connect(u, name, namelen, forced_isn); + return srt::CUDT::connect(u, name, namelen, forced_isn); } int send(SRTSOCKET u, const char* buf, int len, int flags) { - return CUDT::send(u, buf, len, flags); + return srt::CUDT::send(u, buf, len, flags); } int recv(SRTSOCKET u, char* buf, int len, int flags) { - return CUDT::recv(u, buf, len, flags); + return srt::CUDT::recv(u, buf, len, flags); } @@ -4316,18 +4315,18 @@ int sendmsg( SRTSOCKET u, const char* buf, int len, int ttl, bool inorder, int64_t srctime) { - return CUDT::sendmsg(u, buf, len, ttl, inorder, srctime); + return srt::CUDT::sendmsg(u, buf, len, ttl, inorder, srctime); } int recvmsg(SRTSOCKET u, char* buf, int len, int64_t& srctime) { - return CUDT::recvmsg(u, buf, len, srctime); + return srt::CUDT::recvmsg(u, buf, len, srctime); } int recvmsg(SRTSOCKET u, char* buf, int len) { int64_t srctime; - return CUDT::recvmsg(u, buf, len, srctime); + return srt::CUDT::recvmsg(u, buf, len, srctime); } int64_t sendfile( @@ -4337,7 +4336,7 @@ int64_t sendfile( int64_t size, int block) { - return CUDT::sendfile(u, ifs, offset, size, block); + return srt::CUDT::sendfile(u, ifs, offset, size, block); } int64_t recvfile( @@ -4347,7 +4346,7 @@ int64_t recvfile( int64_t size, int block) { - return CUDT::recvfile(u, ofs, offset, size, block); + return srt::CUDT::recvfile(u, ofs, offset, size, block); } int64_t sendfile2( @@ -4358,7 +4357,7 @@ int64_t sendfile2( int block) { fstream ifs(path, ios::binary | ios::in); - int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block); + int64_t ret = srt::CUDT::sendfile(u, ifs, *offset, size, block); ifs.close(); return ret; } @@ -4371,7 +4370,7 @@ int64_t recvfile2( int block) { fstream ofs(path, ios::binary | ios::out); - int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block); + int64_t ret = srt::CUDT::recvfile(u, ofs, *offset, size, block); ofs.close(); return ret; } @@ -4383,7 +4382,7 @@ int select( UDSET* exceptfds, const struct timeval* timeout) { - return CUDT::select(nfds, readfds, writefds, exceptfds, timeout); + return srt::CUDT::select(nfds, readfds, writefds, exceptfds, timeout); } int selectEx( @@ -4393,47 +4392,47 @@ int selectEx( vector* exceptfds, int64_t msTimeOut) { - return CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + return srt::CUDT::selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } int epoll_create() { - return CUDT::epoll_create(); + return srt::CUDT::epoll_create(); } int epoll_clear_usocks(int eid) { - return CUDT::epoll_clear_usocks(eid); + return srt::CUDT::epoll_clear_usocks(eid); } int epoll_add_usock(int eid, SRTSOCKET u, const int* events) { - return CUDT::epoll_add_usock(eid, u, events); + return srt::CUDT::epoll_add_usock(eid, u, events); } int epoll_add_ssock(int eid, SYSSOCKET s, const int* events) { - return CUDT::epoll_add_ssock(eid, s, events); + return srt::CUDT::epoll_add_ssock(eid, s, events); } int epoll_update_usock(int eid, SRTSOCKET u, const int* events) { - return CUDT::epoll_update_usock(eid, u, events); + return srt::CUDT::epoll_update_usock(eid, u, events); } int epoll_update_ssock(int eid, SYSSOCKET s, const int* events) { - return CUDT::epoll_update_ssock(eid, s, events); + return srt::CUDT::epoll_update_ssock(eid, s, events); } int epoll_remove_usock(int eid, SRTSOCKET u) { - return CUDT::epoll_remove_usock(eid, u); + return srt::CUDT::epoll_remove_usock(eid, u); } int epoll_remove_ssock(int eid, SYSSOCKET s) { - return CUDT::epoll_remove_ssock(eid, s); + return srt::CUDT::epoll_remove_ssock(eid, s); } int epoll_wait( @@ -4444,34 +4443,9 @@ int epoll_wait( set* lrfds, set* lwfds) { - return CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); + return srt::CUDT::epoll_wait(eid, readfds, writefds, msTimeOut, lrfds, lwfds); } -/* - -#define SET_RESULT(val, num, fds, it) \ - if (val != NULL) \ - { \ - if (val->empty()) \ - { \ - if (num) *num = 0; \ - } \ - else \ - { \ - if (*num > static_cast(val->size())) \ - *num = val->size(); \ - int count = 0; \ - for (it = val->begin(); it != val->end(); ++ it) \ - { \ - if (count >= *num) \ - break; \ - fds[count ++] = *it; \ - } \ - } \ - } - -*/ - template inline void set_result(set* val, int* num, SOCKTYPE* fds) { @@ -4524,7 +4498,7 @@ int epoll_wait2( if ((lwfds != NULL) && (lwnum != NULL)) lwval = &lwset; - int ret = CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); + int ret = srt::CUDT::epoll_wait(eid, rval, wval, msTimeOut, lrval, lwval); if (ret > 0) { //set::const_iterator i; @@ -4544,32 +4518,32 @@ int epoll_wait2( int epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut) { - return CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); + return srt::CUDT::epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); } int epoll_release(int eid) { - return CUDT::epoll_release(eid); + return srt::CUDT::epoll_release(eid); } ERRORINFO& getlasterror() { - return CUDT::getlasterror(); + return srt::CUDT::getlasterror(); } int getlasterror_code() { - return CUDT::getlasterror().getErrorCode(); + return srt::CUDT::getlasterror().getErrorCode(); } const char* getlasterror_desc() { - return CUDT::getlasterror().getErrorMessage(); + return srt::CUDT::getlasterror().getErrorMessage(); } int getlasterror_errno() { - return CUDT::getlasterror().getErrno(); + return srt::CUDT::getlasterror().getErrno(); } // Get error string of a given error code @@ -4581,12 +4555,12 @@ const char* geterror_desc(int code, int err) int bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear) { - return CUDT::bstats(u, perf, clear); + return srt::CUDT::bstats(u, perf, clear); } SRT_SOCKSTATUS getsockstate(SRTSOCKET u) { - return CUDT::getsockstate(u); + return srt::CUDT::getsockstate(u); } } // namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index 4ca299b24..3ef75c6e7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -72,6 +72,8 @@ modified by // Please refer to structure and locking information provided in the // docs/dev/low-level-info.md document. +namespace srt { + class CUDT; class CUDTSocket @@ -107,7 +109,7 @@ class CUDTSocket /// of sockets in order to prevent other methods from accessing invalid address. /// A timer is started and the socket will be removed after approximately /// 1 second (see CUDTUnited::checkBrokenSockets()). - srt::sync::steady_clock::time_point m_tsClosureTimeStamp; + sync::steady_clock::time_point m_tsClosureTimeStamp; sockaddr_any m_SelfAddr; //< local address of the socket sockaddr_any m_PeerAddr; //< peer address of the socket @@ -117,7 +119,7 @@ class CUDTSocket SRTSOCKET m_PeerID; //< peer socket ID #if ENABLE_EXPERIMENTAL_BONDING - srt::groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member + groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't #endif @@ -125,10 +127,10 @@ class CUDTSocket CUDT* m_pUDT; //< pointer to the UDT entity - std::set m_QueuedSockets; //< set of connections waiting for accept() + std::set m_QueuedSockets; //< set of connections waiting for accept() - srt::sync::Condition m_AcceptCond; //< used to block "accept" call - srt::sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond + sync::Condition m_AcceptCond; //< used to block "accept" call + sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond unsigned int m_uiBackLog; //< maximum number of connections in queue @@ -143,9 +145,9 @@ class CUDTSocket // When deleting, you simply "unsubscribe" yourself from the multiplexer, which // will unref it and remove the list element by the iterator kept by the // socket. - int m_iMuxID; //< multiplexer ID + int m_iMuxID; //< multiplexer ID - srt::sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect + sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect CUDT& core() { return *m_pUDT; } @@ -346,12 +348,12 @@ friend class CRendezvousQueue; groups_t m_Groups; #endif - srt::sync::Mutex m_GlobControlLock; // used to synchronize UDT API + sync::Mutex m_GlobControlLock; // used to synchronize UDT API - srt::sync::Mutex m_IDLock; // used to synchronize ID generation + sync::Mutex m_IDLock; // used to synchronize ID generation - SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID - SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one + SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID + SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one std::map > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn @@ -394,7 +396,7 @@ friend class CRendezvousQueue; // We have a guarantee that if `group` was set // as non-NULL here, it is also acquired and will not // be deleted until this busy flag is set back to false. - srt::sync::ScopedLock cgroup (*group->exp_groupLock()); + sync::ScopedLock cgroup (*group->exp_groupLock()); group->apiRelease(); // Only now that the group lock is lifted, can the // group be now deleted and this pointer potentially dangling @@ -408,21 +410,21 @@ friend class CRendezvousQueue; private: std::map m_mMultiplexer; // UDP multiplexer - srt::sync::Mutex m_MultiplexerLock; + sync::Mutex m_MultiplexerLock; private: CCache* m_pCache; // UDT network information cache private: volatile bool m_bClosing; - srt::sync::Mutex m_GCStopLock; - srt::sync::Condition m_GCStopCond; + sync::Mutex m_GCStopLock; + sync::Condition m_GCStopCond; - srt::sync::Mutex m_InitLock; + sync::Mutex m_InitLock; int m_iInstanceCount; // number of startup() called by application bool m_bGCStatus; // if the GC thread is working (true) - srt::sync::CThread m_GCThread; + sync::CThread m_GCThread; static void* garbageCollect(void*); sockets_t m_ClosedSockets; // temporarily store closed sockets @@ -440,4 +442,6 @@ friend class CRendezvousQueue; CUDTUnited& operator=(const CUDTUnited&); }; +} // namespace srt + #endif diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index bce9922b6..5a756792e 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -61,6 +61,7 @@ modified by using namespace std; using namespace srt_logging; +using namespace srt; using namespace srt::sync; // You can change this value at build config by using "ENFORCE" options. @@ -317,7 +318,7 @@ void CSndBuffer::updateInputRate(const steady_clock::time_point& time, int pkts, if (early_update || period_us > m_InRatePeriod) { // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBytesCount += (m_iInRatePktsCount * srt::CPacket::SRT_DATA_HDR_SIZE); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount @@ -410,7 +411,7 @@ steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& bloc return block.m_tsOriginTime; } -int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read if (m_pCurrBlock == m_pLastBlock) @@ -511,7 +512,7 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) { int32_t& msgno_bitset = w_packet.m_iMsgNo; @@ -927,7 +928,7 @@ int CRcvBuffer::readBuffer(char* data, int len) return -1; } - const CPacket& pkt = m_pUnit[p]->m_Packet; + const srt::CPacket& pkt = m_pUnit[p]->m_Packet; if (bTsbPdEnabled) { @@ -996,7 +997,7 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) continue; } - const CPacket& pkt = m_pUnit[p]->m_Packet; + const srt::CPacket& pkt = m_pUnit[p]->m_Packet; #if ENABLE_LOGGING trace_seq = pkt.getSeqNo(); @@ -1436,7 +1437,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& if (m_tsbpd.isEnabled()) { - const CPacket* pkt = getRcvReadyPacket(seqdistance); + const srt::CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) { HLOGC(brlog.Debug, log << "isRcvDataReady: packet NOT extracted."); @@ -1573,7 +1574,7 @@ void CRcvBuffer::reportBufferStats() const uint64_t lower_time = low_ts; if (lower_time > upper_time) - upper_time += uint64_t(CPacket::MAX_TIMESTAMP) + 1; + upper_time += uint64_t(srt::CPacket::MAX_TIMESTAMP) + 1; int32_t timespan = upper_time - lower_time; int seqspan = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 7f7680b8d..9a92e25b0 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -145,7 +145,7 @@ class CSndBuffer /// @param [out] origintime origin time stamp of the message /// @param [in] kflags Odd|Even crypto key flag /// @return Actual length of data read. - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(srt::CPacket& w_packet, time_point& w_origintime, int kflgs); /// Find data position to pack a DATA packet for a retransmission. /// @param [out] data the pointer to the data position. @@ -154,7 +154,7 @@ class CSndBuffer /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message /// @return Actual length of data read. - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) @@ -288,7 +288,7 @@ class CRcvBuffer /// Construct the buffer. /// @param [in] queue CUnitQueue that actually holds the units (packets) /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + CRcvBuffer(srt::CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); ~CRcvBuffer(); public: @@ -296,7 +296,7 @@ class CRcvBuffer /// @param [in] unit pointer to a data unit containing new packet /// @param [in] offset offset from last ACK point. /// @return 0 is success, -1 if data is repeated. - int addData(CUnit* unit, int offset); + int addData(srt::CUnit* unit, int offset); /// Read data into a user buffer. /// @param [in] data pointer to user buffer. @@ -402,7 +402,7 @@ class CRcvBuffer bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } - CPacket* getRcvReadyPacket(int32_t seqdistance); + srt::CPacket* getRcvReadyPacket(int32_t seqdistance); /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay @@ -462,7 +462,7 @@ class CRcvBuffer /// data. size_t freeUnitAt(size_t p) { - CUnit* u = m_pUnit[p]; + srt::CUnit* u = m_pUnit[p]; m_pUnit[p] = NULL; size_t rmbytes = u->m_Packet.getLength(); m_pUnitQueue->makeUnitFree(u); @@ -528,9 +528,9 @@ class CRcvBuffer } private: - CUnit** m_pUnit; // Array of pointed units collected in the buffer + srt::CUnit** m_pUnit; // Array of pointed units collected in the buffer const int m_iSize; // Size of the internal array of CUnit* items - CUnitQueue* m_pUnitQueue; // the shared unit queue + srt::CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartPos; // HEAD: first packet available for reading int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index f78689a3a..c7b6e93f3 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -71,6 +71,8 @@ modified by using namespace std; using namespace srt_logging; +namespace srt { + #ifdef _WIN32 // use INVALID_SOCKET, as provided #else @@ -136,17 +138,18 @@ static int set_cloexec(int fd, int set) { #endif // if defined(_AIX) ... #endif // ifndef _WIN32 #endif // if ENABLE_CLOEXEC +} // namespace srt -CChannel::CChannel() +srt::CChannel::CChannel() :m_iSocket(INVALID_SOCKET) { } -CChannel::~CChannel() +srt::CChannel::~CChannel() { } -void CChannel::createSocket(int family) +void srt::CChannel::createSocket(int family) { #if ENABLE_SOCK_CLOEXEC bool cloexec_flag = false; @@ -198,7 +201,7 @@ void CChannel::createSocket(int family) } -void CChannel::open(const sockaddr_any& addr) +void srt::CChannel::open(const sockaddr_any& addr) { createSocket(addr.family()); socklen_t namelen = addr.size(); @@ -212,7 +215,7 @@ void CChannel::open(const sockaddr_any& addr) setUDPSockOpt(); } -void CChannel::open(int family) +void srt::CChannel::open(int family) { createSocket(family); @@ -254,7 +257,7 @@ void CChannel::open(int family) setUDPSockOpt(); } -void CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) +void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) { // The getsockname() call is done before calling it and the // result is placed into udpsocks_addr. @@ -263,7 +266,7 @@ void CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) setUDPSockOpt(); } -void CChannel::setUDPSockOpt() +void srt::CChannel::setUDPSockOpt() { #if defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value @@ -390,7 +393,7 @@ void CChannel::setUDPSockOpt() #endif } -void CChannel::close() const +void srt::CChannel::close() const { #ifndef _WIN32 ::close(m_iSocket); @@ -399,26 +402,26 @@ void CChannel::close() const #endif } -int CChannel::getSndBufSize() +int srt::CChannel::getSndBufSize() { socklen_t size = (socklen_t) sizeof m_mcfg.iUDPSndBufSize; ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*) &m_mcfg.iUDPSndBufSize, &size); return m_mcfg.iUDPSndBufSize; } -int CChannel::getRcvBufSize() +int srt::CChannel::getRcvBufSize() { socklen_t size = (socklen_t) sizeof m_mcfg.iUDPRcvBufSize; ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*) &m_mcfg.iUDPRcvBufSize, &size); return m_mcfg.iUDPRcvBufSize; } -void CChannel::setConfig(const CSrtMuxerConfig& config) +void srt::CChannel::setConfig(const CSrtMuxerConfig& config) { m_mcfg = config; } -int CChannel::getIpTTL() const +int srt::CChannel::getIpTTL() const { if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -441,7 +444,7 @@ int CChannel::getIpTTL() const return m_mcfg.iIpTTL; } -int CChannel::getIpToS() const +int srt::CChannel::getIpToS() const { if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -467,7 +470,7 @@ int CChannel::getIpToS() const } #ifdef SRT_ENABLE_BINDTODEVICE -bool CChannel::getBind(char* dst, size_t len) +bool srt::CChannel::getBind(char* dst, size_t len) { if (m_iSocket == INVALID_SOCKET) return false; // No socket to get data from @@ -485,7 +488,7 @@ bool CChannel::getBind(char* dst, size_t len) } #endif -int CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const +int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; @@ -496,7 +499,7 @@ int CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const return -1; } -int CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const +int srt::CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; @@ -508,7 +511,7 @@ int CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) return -1; } -void CChannel::getSockAddr(sockaddr_any& w_addr) const +void srt::CChannel::getSockAddr(sockaddr_any& w_addr) const { // The getsockname function requires only to have enough target // space to copy the socket name, it doesn't have to be correlated @@ -519,7 +522,7 @@ void CChannel::getSockAddr(sockaddr_any& w_addr) const w_addr.len = namelen; } -void CChannel::getPeerAddr(sockaddr_any& w_addr) const +void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const { socklen_t namelen = (socklen_t) w_addr.storage_size(); ::getpeername(m_iSocket, (w_addr.get()), (&namelen)); @@ -527,7 +530,7 @@ void CChannel::getPeerAddr(sockaddr_any& w_addr) const } -int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const +int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { HLOGC(kslog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID @@ -615,7 +618,7 @@ int CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const return res; } -EReadStatus CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const +EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const { EReadStatus status = RST_OK; int msg_flags = 0; diff --git a/srtcore/channel.h b/srtcore/channel.h index 6ec51c875..ad6b3f785 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -59,6 +59,8 @@ modified by #include "socketconfig.h" #include "netinet_any.h" +namespace srt { + class CChannel { void createSocket(int family); @@ -114,14 +116,14 @@ class CChannel /// @param [in] packet reference to a CPacket entity. /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, CPacket& packet) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; /// Receive a packet from the channel and record the source address. /// @param [in] addr pointer to the source address. /// @param [in] packet reference to a CPacket entity. /// @return Actual size of data received. - EReadStatus recvfrom(sockaddr_any& addr, CPacket& packet) const; + EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; void setConfig(const CSrtMuxerConfig& config); @@ -160,5 +162,6 @@ class CChannel sockaddr_any m_BindAddr; }; +} // namespace srt #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 31b1bebe0..389933b26 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -70,6 +70,7 @@ modified by #include // SysStrError +using namespace srt; using namespace srt::sync; namespace srt_logging { diff --git a/srtcore/common.h b/srtcore/common.h index 8a47a0c6c..f6fdba44a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -311,7 +311,9 @@ enum EInitEvent TEV_INIT_OHEADBW }; -class CPacket; +namespace srt { + class CPacket; +} // XXX Use some more standard less hand-crafted solution, if possible // XXX Consider creating a mapping between TEV_* values and associated types, @@ -322,7 +324,7 @@ struct EventVariant enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type; union U { - const CPacket* packet; + const srt::CPacket* packet; int32_t ack; struct { @@ -341,7 +343,7 @@ struct EventVariant // Note: UNDEFINED and ARRAY don't have assignment operator. // For ARRAY you'll use 'set' function. For UNDEFINED there's nothing. - explicit EventVariant(const CPacket* arg) + explicit EventVariant(const srt::CPacket* arg) { type = PACKET; u.packet = arg; @@ -430,7 +432,7 @@ class EventArgType; // use a full-templated version. TBD. template<> struct EventVariant::VariantFor { - typedef const CPacket* type; + typedef const srt::CPacket* type; static type U::*field() {return &U::packet;} }; @@ -1406,7 +1408,7 @@ inline std::string SrtVersionString(int version) return buf; } -bool SrtParseConfig(std::string s, SrtConfig& w_config); +bool SrtParseConfig(std::string s, srt::SrtConfig& w_config); struct PacketMetric { diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index fdf9ddb0b..998256216 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -34,6 +34,7 @@ #include "logging.h" using namespace std; +using namespace srt; using namespace srt::sync; using namespace srt_logging; diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 6419605c0..0ad835783 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -16,10 +16,12 @@ #include #include -class CUDT; +namespace srt { + class CUDT; +} class SrtCongestionControlBase; -typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent); +typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent); class SrtCongestion { @@ -97,7 +99,7 @@ class SrtCongestion // in appropriate time. It should select appropriate // congctl basing on the value in selector, then // pin oneself in into CUDT for receiving event signals. - bool configure(CUDT* parent); + bool configure(srt::CUDT* parent); // This function will intentionally delete the contained object. // This makes future calls to ready() return false. Calling @@ -129,13 +131,15 @@ class SrtCongestion }; }; -class CPacket; +namespace srt { + class CPacket; +} class SrtCongestionControlBase { protected: // Here can be some common fields - CUDT* m_parent; + srt::CUDT* m_parent; double m_dPktSndPeriod; double m_dCWndSize; @@ -150,7 +154,7 @@ class SrtCongestionControlBase //char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead. // Constructor in protected section so that this class is semi-abstract. - SrtCongestionControlBase(CUDT* parent); + SrtCongestionControlBase(srt::CUDT* parent); public: // This could be also made abstract, but this causes a linkage @@ -192,7 +196,7 @@ class SrtCongestionControlBase // Arg 2: value calculated out of CUDT's m_config.llInputBW and m_config.iOverheadBW. virtual void updateBandwidth(int64_t, int64_t) {} - virtual bool needsQuickACK(const CPacket&) + virtual bool needsQuickACK(const srt::CPacket&) { return false; } diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f6c6cac0a..1e9d2f052 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -83,10 +83,12 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -CUDTUnited CUDT::s_UDTUnited; +namespace srt { + CUDTUnited CUDT::s_UDTUnited; +} -const SRTSOCKET UDT::INVALID_SOCK = CUDT::INVALID_SOCK; -const int UDT::ERROR = CUDT::ERROR; +const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; +const int UDT::ERROR = srt::CUDT::ERROR; //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -221,7 +223,7 @@ const SrtOptionAction s_sockopt_action; } // namespace srt -void CUDT::construct() +void srt::CUDT::construct() { m_pSndBuffer = NULL; m_pRcvBuffer = NULL; @@ -271,7 +273,7 @@ void CUDT::construct() // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } -CUDT::CUDT(CUDTSocket* parent): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) { construct(); @@ -294,7 +296,7 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent) } -CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) { construct(); @@ -330,7 +332,7 @@ CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) m_pCache = ancestor.m_pCache; } -CUDT::~CUDT() +srt::CUDT::~CUDT() { // release mutex/condtion variables destroySynch(); @@ -344,7 +346,7 @@ CUDT::~CUDT() delete m_pRNode; } -void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) +void srt::CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) { if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -403,7 +405,7 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) } } -void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) +void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) { ScopedLock cg(m_ConnectionLock); @@ -782,7 +784,7 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) #if ENABLE_EXPERIMENTAL_BONDING -SRT_ERRNO CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) +SRT_ERRNO srt::CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) { SRT_SOCKOPT this_opt = SRTO_VERSION; for (size_t i = 0; i < opt.options.size(); ++i) @@ -796,7 +798,7 @@ SRT_ERRNO CUDT::applyMemberConfigObject(const SRT_SocketOptionObject& opt) } #endif -bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) +bool srt::CUDT::setstreamid(SRTSOCKET u, const std::string &sid) { CUDT *that = getUDTHandle(u); if (!that) @@ -812,7 +814,7 @@ bool CUDT::setstreamid(SRTSOCKET u, const std::string &sid) return true; } -std::string CUDT::getstreamid(SRTSOCKET u) +string srt::CUDT::getstreamid(SRTSOCKET u) { CUDT *that = getUDTHandle(u); if (!that) @@ -823,7 +825,7 @@ std::string CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. -void CUDT::clearData() +void srt::CUDT::clearData() { // Initial sequence number, loss, acknowledgement, etc. int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; @@ -912,7 +914,7 @@ void CUDT::clearData() m_tsRcvPeerStartTime = steady_clock::time_point(); } -void CUDT::open() +void srt::CUDT::open() { ScopedLock cg(m_ConnectionLock); @@ -965,7 +967,7 @@ void CUDT::open() m_bOpened = true; } -void CUDT::setListenState() +void srt::CUDT::setListenState() { ScopedLock cg(m_ConnectionLock); @@ -986,7 +988,7 @@ void CUDT::setListenState() m_bListening = true; } -size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) +size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) { if (srtlen < SRT_HS_E_SIZE) { @@ -1014,7 +1016,7 @@ size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, } } -size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) +size_t srt::CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // INITIATOR sends HSREQ. @@ -1077,7 +1079,7 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unu return 3; } -size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) +size_t srt::CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // Setting m_tsRcvPeerStartTime is done in processSrtMsg_HSREQ(), so // this condition will be skipped only if this function is called without @@ -1185,7 +1187,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unu return 3; } -size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) +size_t srt::CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) { size_t srtlen = fillSrtHandshake(srtdata, size, cmd, handshakeVersion()); HLOGF(cnlog.Debug, @@ -1201,7 +1203,7 @@ size_t CUDT::prepareSrtHsMsg(int cmd, uint32_t *srtdata, size_t size) return srtlen; } -void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) +void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) { CPacket srtpkt; int32_t srtcmd = (int32_t)cmd; @@ -1259,7 +1261,7 @@ void CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) } } -size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& str) +size_t srt::CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& str) { uint32_t* space = pcmdspec + 1; size_t wordsize = (str.size() + 3) / 4; @@ -1278,7 +1280,7 @@ size_t CUDT::fillHsExtConfigString(uint32_t* pcmdspec, int cmd, const string& st #if ENABLE_EXPERIMENTAL_BONDING // [[using locked(m_parent->m_ControlLock)]] // [[using locked(s_UDTUnited.m_GlobControlLock)]] -size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) +size_t srt::CUDT::fillHsExtGroup(uint32_t* pcmdspec) { SRT_ASSERT(m_parent->m_GroupOf != NULL); uint32_t* space = pcmdspec + 1; @@ -1320,7 +1322,7 @@ size_t CUDT::fillHsExtGroup(uint32_t* pcmdspec) } #endif -size_t CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) +size_t srt::CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) { uint32_t* space = pcmdspec + 1; @@ -1349,7 +1351,7 @@ size_t CUDT::fillHsExtKMREQ(uint32_t* pcmdspec, size_t ki) return ra_size; } -size_t CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t kmdata_wordsize) +size_t srt::CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t kmdata_wordsize) { uint32_t* space = pcmdspec + 1; const uint32_t failure_kmrsp[] = {SRT_KM_S_UNSECURED}; @@ -1396,7 +1398,7 @@ size_t CUDT::fillHsExtKMRSP(uint32_t* pcmdspec, const uint32_t* kmdata, size_t k // PREREQUISITE: // pkt must be set the buffer and configured for UMSG_HANDSHAKE. // Note that this function replaces also serialization for the HSv4. -bool CUDT::createSrtHandshake( +bool srt::CUDT::createSrtHandshake( int srths_cmd, int srtkm_cmd, const uint32_t* kmdata, @@ -1939,7 +1941,7 @@ RttTracer s_rtt_trace; #endif -bool CUDT::processSrtMsg(const CPacket *ctrlpkt) +bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) { uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); @@ -2021,7 +2023,7 @@ bool CUDT::processSrtMsg(const CPacket *ctrlpkt) return true; } -int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) +int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) { // Set this start time in the beginning, regardless as to whether TSBPD is being // used or not. This must be done in the Initiator as well as Responder. @@ -2235,7 +2237,7 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t return SRT_CMD_HSRSP; } -int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) +int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t ts, int hsv) { // XXX Check for mis-version // With HSv4 we accept only version less than 1.3.0 @@ -2381,7 +2383,7 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t } // This function is called only when the URQ_CONCLUSION handshake has been received from the peer. -bool CUDT::interpretSrtHandshake(const CHandShake& hs, +bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, uint32_t* out_data, size_t* pw_len) @@ -2894,7 +2896,7 @@ bool CUDT::interpretSrtHandshake(const CHandShake& hs, return true; } -bool CUDT::checkApplyFilterConfig(const std::string &confstr) +bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) { SrtFilterConfig cfg; if (!ParseFilterConfig(confstr, (cfg))) @@ -2976,7 +2978,7 @@ bool CUDT::checkApplyFilterConfig(const std::string &confstr) } #if ENABLE_EXPERIMENTAL_BONDING -bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UNUSED, int hsreq_type_cmd SRT_ATR_UNUSED) +bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UNUSED, int hsreq_type_cmd SRT_ATR_UNUSED) { // `data_size` isn't checked because we believe it's checked earlier. // Also this code doesn't predict to get any other format than the official one, @@ -3147,7 +3149,7 @@ bool CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_ATR_UN // exclusively on the listener side (HSD_RESPONDER, HSv5+). // [[using locked(s_UDTUnited.m_GlobControlLock)]] -SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t link_flags) +SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t link_flags) { // Note: This function will lock pg->m_GroupLock! @@ -3245,7 +3247,7 @@ SRTSOCKET CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint32_t l return gp->id(); } -void CUDT::synchronizeWithGroup(CUDTGroup* gp) +void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { ScopedLock gl (*gp->exp_groupLock()); @@ -3344,7 +3346,7 @@ void CUDT::synchronizeWithGroup(CUDTGroup* gp) } #endif -void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) +void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { ScopedLock cg (m_ConnectionLock); @@ -3723,7 +3725,7 @@ void CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) } // Asynchronous connection -EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT +EConnectStatus srt::CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEPT { EConnectStatus cst = CONN_CONTINUE; CUDTException e; @@ -3740,7 +3742,7 @@ EConnectStatus CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NOEXCEP return cst; } -bool CUDT::processAsyncConnectRequest(EReadStatus rst, +bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr) @@ -3834,7 +3836,7 @@ bool CUDT::processAsyncConnectRequest(EReadStatus rst, return status; } -void CUDT::cookieContest() +void srt::CUDT::cookieContest() { if (m_SrtHsSide != HSD_DRAW) return; @@ -3899,7 +3901,7 @@ void CUDT::cookieContest() // - There's no KMX (including first responder's handshake in rendezvous). This writes 0 to w_kmdatasize. // - The encryption status is failure. Respond with fail code and w_kmdatasize = 1. // - The last KMX was successful. Respond with the original kmdata and their size in w_kmdatasize. -EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) +EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) { // If the last CONCLUSION message didn't contain the KMX extension, there's // no key recorded yet, so it can't be extracted. Mark this w_kmdatasize empty though. @@ -3973,7 +3975,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) return CONN_ACCEPT; } -EConnectStatus CUDT::processRendezvous( +EConnectStatus srt::CUDT::processRendezvous( const CPacket& response, const sockaddr_any& serv_addr, EReadStatus rst, CPacket& w_reqpkt) { @@ -4243,7 +4245,7 @@ EConnectStatus CUDT::processRendezvous( } // [[using locked(m_ConnectionLock)]]; -EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTException* eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTException* eout) ATR_NOEXCEPT { // NOTE: ASSUMED LOCK ON: m_ConnectionLock. @@ -4475,7 +4477,7 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti return postConnect(response, false, eout); } -bool CUDT::applyResponseSettings() ATR_NOEXCEPT +bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT { if (!m_ConnRes.valid()) { @@ -4505,7 +4507,7 @@ bool CUDT::applyResponseSettings() ATR_NOEXCEPT return true; } -EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT { if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. @@ -4713,7 +4715,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE return CONN_ACCEPT; } -void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield) +void srt::CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t typefield) { int enc_flags = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(typefield); @@ -4759,7 +4761,7 @@ void CUDT::checkUpdateCryptoKeyLen(const char *loghdr SRT_ATR_UNUSED, int32_t ty } // Rendezvous -void CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extension, bool& w_needs_hsrsp) +void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extension, bool& w_needs_hsrsp) { UDTRequestType req = m_ConnRes.m_iReqType; int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); @@ -5123,7 +5125,7 @@ void CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_extens * This thread runs only if TsbPd mode is enabled * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay. */ -void *CUDT::tsbpd(void *param) +void * srt::CUDT::tsbpd(void *param) { CUDT *self = (CUDT *)param; @@ -5332,7 +5334,7 @@ void *CUDT::tsbpd(void *param) return NULL; } -void CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) +void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { /* Update drop/skip stats */ enterCS(m_StatsLock); @@ -5347,7 +5349,7 @@ void CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) dropFromLossLists(lastack, CSeqNo::decseq(skiptoseqno)); //remove(from,to-inclusive) } -bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) +bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout) { // This will be lazily created due to being the common // code with HSv5 rendezvous, in which this will be run @@ -5409,7 +5411,7 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD return true; } -void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) +void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) { // this is a reponse handshake w_hs.m_iReqType = URQ_CONCLUSION; @@ -5428,7 +5430,7 @@ void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); } -void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) +void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << "acceptAndRespond: setting up data according to handshake"); @@ -5610,7 +5612,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // be created, as this happens before the completion of the connection (and // therefore configuration of the crypter object), which can only take place upon // reception of CONCLUSION response from the listener. -bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) +bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional) { // Lazy initialization if (m_pCryptoControl) @@ -5636,7 +5638,7 @@ bool CUDT::createCrypter(HandshakeSide side, bool bidirectional) return m_pCryptoControl->init(side, bidirectional); } -SRT_REJECT_REASON CUDT::setupCC() +SRT_REJECT_REASON srt::CUDT::setupCC() { // Prepare configuration object, // Create the CCC object and configure it. @@ -5719,7 +5721,7 @@ SRT_REJECT_REASON CUDT::setupCC() return SRT_REJ_UNKNOWN; } -void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) +void srt::CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) { // Do a fast pre-check first - this simply declares that agent uses HSv5 // and the legacy SRT Handshake is not to be done. Second check is whether @@ -5769,7 +5771,7 @@ void CUDT::considerLegacySrtHandshake(const steady_clock::time_point &timebase) sendSrtMsg(SRT_CMD_HSREQ); } -void CUDT::checkSndTimers(Whether2RegenKm regen) +void srt::CUDT::checkSndTimers(Whether2RegenKm regen) { if (m_SrtHsSide == HSD_INITIATOR) { @@ -5797,7 +5799,7 @@ void CUDT::checkSndTimers(Whether2RegenKm regen) } } -void CUDT::addressAndSend(CPacket& w_pkt) +void srt::CUDT::addressAndSend(CPacket& w_pkt) { w_pkt.m_iID = m_PeerID; setPacketTS(w_pkt, steady_clock::now()); @@ -5811,7 +5813,7 @@ void CUDT::addressAndSend(CPacket& w_pkt) } // [[using maybe_locked(m_GlobControlLock, if called from GC)]] -bool CUDT::closeInternal() +bool srt::CUDT::closeInternal() { // NOTE: this function is called from within the garbage collector thread. @@ -5987,7 +5989,7 @@ bool CUDT::closeInternal() return true; } -int CUDT::receiveBuffer(char *data, int len) +int srt::CUDT::receiveBuffer(char *data, int len) { if (!m_CongCtl->checkTransArgs(SrtCongestion::STA_BUFFER, SrtCongestion::STAD_RECV, data, len, SRT_MSGTTL_INF, false)) throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); @@ -6112,7 +6114,7 @@ int CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -void CUDT::checkNeedDrop(bool& w_bCongestion) +void srt::CUDT::checkNeedDrop(bool& w_bCongestion) { if (!m_bPeerTLPktDrop) return; @@ -6209,7 +6211,7 @@ void CUDT::checkNeedDrop(bool& w_bCongestion) } } -int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) +int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; mctrl.msgttl = msttl; @@ -6221,7 +6223,7 @@ int CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t sr // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::send, // which is the only case when the m_parent->m_GroupOf is not NULL. -int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) +int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { bool bCongestion = false; @@ -6508,13 +6510,13 @@ int CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) return size; } -int CUDT::recv(char* data, int len) +int srt::CUDT::recv(char* data, int len) { SRT_MSGCTRL mctrl = srt_msgctrl_default; return recvmsg2(data, len, (mctrl)); } -int CUDT::recvmsg(char* data, int len, int64_t& srctime) +int srt::CUDT::recvmsg(char* data, int len, int64_t& srctime) { SRT_MSGCTRL mctrl = srt_msgctrl_default; int res = recvmsg2(data, len, (mctrl)); @@ -6525,7 +6527,7 @@ int CUDT::recvmsg(char* data, int len, int64_t& srctime) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]] // GroupLock is applied when this function is called from inside CUDTGroup::recv, // which is the only case when the m_parent->m_GroupOf is not NULL. -int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) +int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) { // Check if the socket is a member of a receiver group. // If so, then reading by receiveMessage is disallowed. @@ -6557,7 +6559,7 @@ int CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) // - 0 - by return value // - 1 - by exception // - 2 - by abort (unused) -int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_exception) +int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_exception) { // Recvmsg isn't restricted to the congctl type, it's the most // basic method of passing the data. You can retrieve data as @@ -6809,7 +6811,7 @@ int CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_excep return res; } -int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) +int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) { if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6931,7 +6933,7 @@ int64_t CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int block) return size - tosend; } -int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) +int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) { if (!m_bConnected || !m_CongCtl.ready()) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); @@ -7050,7 +7052,7 @@ int64_t CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int block) return size - torecv; } -void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) +void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) { if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); @@ -7242,7 +7244,7 @@ void CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) } } -bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) +bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) { // Special things that must be done HERE, not in SrtCongestion, // because it involves the input buffer in CUDT. It would be @@ -7360,7 +7362,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) return true; } -void CUDT::initSynch() +void srt::CUDT::initSynch() { setupMutex(m_SendBlockLock, "SendBlock"); setupCond(m_SendBlockCond, "SendBlock"); @@ -7375,7 +7377,7 @@ void CUDT::initSynch() setupCond(m_RcvTsbPdCond, "RcvTsbPd"); } -void CUDT::destroySynch() +void srt::CUDT::destroySynch() { releaseMutex(m_SendBlockLock); @@ -7400,7 +7402,7 @@ void CUDT::destroySynch() releaseCond(m_RcvTsbPdCond); } -void CUDT::releaseSynch() +void srt::CUDT::releaseSynch() { SRT_ASSERT(m_bClosing); // wake up user calls @@ -7430,7 +7432,7 @@ void CUDT::releaseSynch() } // [[using locked(m_RcvBufferLock)]]; -int32_t CUDT::ackDataUpTo(int32_t ack) +int32_t srt::CUDT::ackDataUpTo(int32_t ack) { int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); @@ -7456,6 +7458,7 @@ int32_t CUDT::ackDataUpTo(int32_t ack) return ack; } +namespace srt { #if ENABLE_HEAVY_LOGGING static void DebugAck(string hdr, int prev, int ack) { @@ -7487,8 +7490,9 @@ static void DebugAck(string hdr, int prev, int ack) #else static inline void DebugAck(string, int, int) {} #endif +} -void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size) +void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, int size) { CPacket ctrlpkt; setPacketTS(ctrlpkt, steady_clock::now()); @@ -7627,7 +7631,7 @@ void CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rparam, m_tsLastSndTime = steady_clock::now(); } -int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) +int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); int32_t ack; @@ -7877,7 +7881,7 @@ int CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } -void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) +void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) { #if ENABLE_EXPERIMENTAL_BONDING // This is for the call of CSndBuffer::getMsgNoAt that returns @@ -7964,7 +7968,7 @@ void CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) leaveCS(m_StatsLock); } -void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point& currtime) +void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point& currtime) { const int32_t* ackdata = (const int32_t*)ctrlpkt.m_pcData; const int32_t ackdata_seqno = ackdata[ACKD_RCVLASTACK]; @@ -8216,7 +8220,7 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point leaveCS(m_StatsLock); } -void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) +void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival) { int32_t ack = 0; @@ -8307,7 +8311,7 @@ void CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsArrival m_iRcvLastAckAck = ack; } -void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) { const int32_t* losslist = (int32_t*)(ctrlpkt.m_pcData); const size_t losslist_len = ctrlpkt.getLength() / 4; @@ -8451,7 +8455,7 @@ void CUDT::processCtrlLossReport(const CPacket& ctrlpkt) leaveCS(m_StatsLock); } -void CUDT::processCtrlHS(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) { CHandShake req; req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); @@ -8562,7 +8566,7 @@ void CUDT::processCtrlHS(const CPacket& ctrlpkt) } } -void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { { const bool using_rexmit_flag = m_bPeerRexmitFlag; @@ -8601,7 +8605,7 @@ void CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void CUDT::processCtrlShutdown() +void srt::CUDT::processCtrlShutdown() { m_bShutdown = true; m_bClosing = true; @@ -8614,7 +8618,7 @@ void CUDT::processCtrlShutdown() completeBrokenConnectionDependencies(SRT_ECONNLOST); // LOCKS! } -void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) +void srt::CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) { HLOGC(inlog.Debug, log << CONID() << "CONTROL EXT MSG RECEIVED:" << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) @@ -8644,7 +8648,7 @@ void CUDT::processCtrlUserDefined(const CPacket& ctrlpkt) } } -void CUDT::processCtrl(const CPacket &ctrlpkt) +void srt::CUDT::processCtrl(const CPacket &ctrlpkt) { // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; @@ -8714,7 +8718,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) } } -void CUDT::updateSrtRcvSettings() +void srt::CUDT::updateSrtRcvSettings() { // CHANGED: we need to apply the tsbpd delay only for socket TSBPD. // For Group TSBPD the buffer will have to deliver packets always on request @@ -8745,7 +8749,7 @@ void CUDT::updateSrtRcvSettings() } } -void CUDT::updateSrtSndSettings() +void srt::CUDT::updateSrtSndSettings() { if (m_bPeerTsbPd) { @@ -8768,7 +8772,7 @@ void CUDT::updateSrtSndSettings() } } -void CUDT::updateAfterSrtHandshake(int hsv) +void srt::CUDT::updateAfterSrtHandshake(int hsv) { HLOGC(cnlog.Debug, log << "updateAfterSrtHandshake: HS version " << hsv); // This is blocked from being run in the "app reader" version because here @@ -8823,7 +8827,7 @@ void CUDT::updateAfterSrtHandshake(int hsv) } } -int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime) +int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime) { // protect m_iSndLastDataAck from updating by ACK processing UniqueLock ackguard(m_RecvAckLock); @@ -8935,7 +8939,7 @@ int CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origintime return 0; } -std::pair CUDT::packData(CPacket& w_packet) +std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; bool probe = false; @@ -9216,7 +9220,7 @@ std::pair CUDT::packData(CPacket& w_packet) } // This is a close request, but called from the -void CUDT::processClose() +void srt::CUDT::processClose() { sendCtrl(UMSG_SHUTDOWN); @@ -9242,7 +9246,7 @@ void CUDT::processClose() CGlobEvent::triggerEvent(); } -void CUDT::sendLossReport(const std::vector > &loss_seqs) +void srt::CUDT::sendLossReport(const std::vector > &loss_seqs) { typedef vector > loss_seqs_t; @@ -9274,7 +9278,7 @@ void CUDT::sendLossReport(const std::vector > &loss_ } -bool CUDT::overrideSndSeqNo(int32_t seq) +bool srt::CUDT::overrideSndSeqNo(int32_t seq) { // This function is intended to be called from the socket // group managmenet functions to synchronize the sequnece in @@ -9325,7 +9329,7 @@ bool CUDT::overrideSndSeqNo(int32_t seq) return true; } -int CUDT::processData(CUnit* in_unit) +int srt::CUDT::processData(CUnit* in_unit) { if (m_bClosing) return -1; @@ -9965,7 +9969,7 @@ int CUDT::processData(CUnit* in_unit) } #if ENABLE_EXPERIMENTAL_BONDING -void CUDT::updateIdleLinkFrom(CUDT* source) +void srt::CUDT::updateIdleLinkFrom(CUDT* source) { ScopedLock lg (m_RecvLock); @@ -9999,7 +10003,7 @@ void CUDT::updateIdleLinkFrom(CUDT* source) // XXX This function is currently unused. It should be fixed and put into use. // See the blocked call in CUDT::processData(). // XXX REVIEW LOCKS WHEN REACTIVATING! -CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) +srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) { // [[using affinity(m_pRcvBuffer->workerThread())]]; CUDT* self = (CUDT*)vself; @@ -10074,7 +10078,7 @@ CUDT::loss_seqs_t CUDT::defaultPacketArrival(void* vself, CPacket& pkt) /// do not include the lacking packet. /// The tolerance is not increased infinitely - it's bordered by iMaxReorderTolerance. /// This value can be set in options - SRT_LOSSMAXTTL. -void CUDT::unlose(const CPacket &packet) +void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); int32_t sequence = packet.m_iSeqNo; @@ -10219,7 +10223,7 @@ breakbreak:; } } -void CUDT::dropFromLossLists(int32_t from, int32_t to) +void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) { ScopedLock lg(m_RcvLossLock); m_pRcvLossList->remove(from, to); @@ -10262,7 +10266,7 @@ void CUDT::dropFromLossLists(int32_t from, int32_t to) } // This function, as the name states, should bake a new cookie. -int32_t CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correction) +int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correction) { static unsigned int distractor = 0; unsigned int rollover = distractor + 10; @@ -10321,7 +10325,7 @@ int32_t CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int correct // and this will be directly passed to the caller. // [[using locked(m_pRcvQueue->m_LSLock)]]; -int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) +int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] @@ -10675,7 +10679,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) return RejectReasonForURQ(hs.m_iReqType); } -void CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) +void srt::CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) { if (lo == hi) lr.push_back(lo); @@ -10686,7 +10690,7 @@ void CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) } } -int CUDT::checkACKTimer(const steady_clock::time_point &currtime) +int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) { int because_decision = BECAUSE_NO_REASON; if (currtime > m_tsNextACKTime // ACK time has come @@ -10725,7 +10729,7 @@ int CUDT::checkACKTimer(const steady_clock::time_point &currtime) return because_decision; } -int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) +int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) { // XXX The problem with working NAKREPORT with SRT_ARQ_ONREQ // is not that it would be inappropriate, but because it's not @@ -10764,7 +10768,7 @@ int CUDT::checkNAKTimer(const steady_clock::time_point& currtime) return debug_decision; } -bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) +bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) { // VERY HEAVY LOGGING #if ENABLE_HEAVY_LOGGING & 1 @@ -10859,7 +10863,7 @@ bool CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_rea return false; } -void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) +void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) { /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. * @@ -10933,7 +10937,7 @@ void CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); } -void CUDT::checkTimers() +void srt::CUDT::checkTimers() { // update CC parameters updateCC(TEV_CHECKTIMER, EventVariant(TEV_CHT_INIT)); @@ -10981,7 +10985,7 @@ void CUDT::checkTimers() } } -void CUDT::updateBrokenConnection() +void srt::CUDT::updateBrokenConnection() { m_bClosing = true; releaseSynch(); @@ -10990,7 +10994,7 @@ void CUDT::updateBrokenConnection() CGlobEvent::triggerEvent(); } -void CUDT::completeBrokenConnectionDependencies(int errorcode) +void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) { int token = -1; @@ -11049,7 +11053,7 @@ void CUDT::completeBrokenConnectionDependencies(int errorcode) #endif } -void CUDT::addEPoll(const int eid) +void srt::CUDT::addEPoll(const int eid) { enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.insert(eid); @@ -11071,7 +11075,7 @@ void CUDT::addEPoll(const int eid) } } -void CUDT::removeEPollEvents(const int eid) +void srt::CUDT::removeEPollEvents(const int eid) { // clear IO events notifications; // since this happens after the epoll ID has been removed, they cannot be set again @@ -11080,14 +11084,14 @@ void CUDT::removeEPollEvents(const int eid) s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } -void CUDT::removeEPollID(const int eid) +void srt::CUDT::removeEPollID(const int eid) { enterCS(s_UDTUnited.m_EPoll.m_EPollLock); m_sPollID.erase(eid); leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); } -void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) +void srt::CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) { if (evt >= TEV_E_SIZE) return; // sanity check @@ -11095,7 +11099,7 @@ void CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) m_Slots[evt].push_back(sl); } -void CUDT::DisconnectSignal(ETransmissionEvent evt) +void srt::CUDT::DisconnectSignal(ETransmissionEvent evt) { if (evt >= TEV_E_SIZE) return; // sanity check @@ -11103,7 +11107,7 @@ void CUDT::DisconnectSignal(ETransmissionEvent evt) m_Slots[evt].clear(); } -void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) +void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) { for (std::vector::iterator i = m_Slots[tev].begin(); i != m_Slots[tev].end(); ++i) { @@ -11111,7 +11115,7 @@ void CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) } } -int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) +int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { CUDTSocket *s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11134,7 +11138,7 @@ int CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) return std::abs(timespan); } -int CUDT::rejectReason(SRTSOCKET u) +int srt::CUDT::rejectReason(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11143,7 +11147,7 @@ int CUDT::rejectReason(SRTSOCKET u) return s->m_pUDT->m_RejectReason; } -int CUDT::rejectReason(SRTSOCKET u, int value) +int srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11156,7 +11160,7 @@ int CUDT::rejectReason(SRTSOCKET u, int value) return 0; } -int64_t CUDT::socketStartTime(SRTSOCKET u) +int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); if (!s || !s->m_pUDT) @@ -11165,7 +11169,7 @@ int64_t CUDT::socketStartTime(SRTSOCKET u) return count_microseconds(s->m_pUDT->m_stats.tsStartTime.time_since_epoch()); } -bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) +bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) { // Prepare the information for the hook. @@ -11266,7 +11270,7 @@ bool CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs return true; } -void CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) +void srt::CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) { // Here can be handled some protocol definition // for extra data sent through keepalive. diff --git a/srtcore/core.h b/srtcore/core.h index 1d75bbaea..2e463423c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -137,14 +137,16 @@ enum SeqPairItems SEQ_BEGIN = 0, SEQ_END = 1, SEQ_SIZE = 2 }; -#if ENABLE_EXPERIMENTAL_BONDING -class CUDTGroup; -#endif // Extended SRT Congestion control class - only an incomplete definition required class CCryptoControl; + +namespace srt { class CUDTUnited; class CUDTSocket; +#if ENABLE_EXPERIMENTAL_BONDING +class CUDTGroup; +#endif // XXX REFACTOR: The 'CUDT' class is to be merged with 'CUDTSocket'. // There's no reason for separating them, there's no case of having them @@ -167,10 +169,10 @@ class CUDT friend class PacketFilter; friend class CUDTGroup; friend struct FByOldestActive; // this functional will use private fields - friend class TestMockCUDT; + friend class TestMockCUDT; // unit tests - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; private: // constructor and desctructor void construct(); @@ -1111,12 +1113,12 @@ class CUDT private: // for UDP multiplexer - CSndQueue* m_pSndQueue; // packet sending queue - CRcvQueue* m_pRcvQueue; // packet receiving queue - sockaddr_any m_PeerAddr; // peer address - uint32_t m_piSelfIP[4]; // local UDP IP address - CSNode* m_pSNode; // node information for UDT list used in snd queue - CRNode* m_pRNode; // node information for UDT list used in rcv queue + CSndQueue* m_pSndQueue; // packet sending queue + CRcvQueue* m_pRcvQueue; // packet receiving queue + sockaddr_any m_PeerAddr; // peer address + uint32_t m_piSelfIP[4]; // local UDP IP address + CSNode* m_pSNode; // node information for UDT list used in snd queue + CRNode* m_pRNode; // node information for UDT list used in rcv queue public: // For SrtCongestion const CSndQueue* sndQueue() { return m_pSndQueue; } @@ -1129,5 +1131,6 @@ class CUDT void removeEPollID(const int eid); }; +} // namespace srt #endif diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 5351e1afe..c7c27abab 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -27,6 +27,7 @@ written by #include "logging.h" #include "core.h" +using namespace srt; using namespace srt_logging; #define SRT_MAX_KMRETRY 10 diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 9d1dafc3d..4e067678b 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -38,6 +38,11 @@ extern Logger cnlog; #endif } +namespace srt +{ + class CUDT; +} + // For KMREQ/KMRSP. Only one field is used. const size_t SRT_KMR_KMSTATE = 0; @@ -49,8 +54,7 @@ enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; class CCryptoControl { -//public: - class CUDT* m_parent; + srt::CUDT* m_parent; SRTSOCKET m_SocketID; size_t m_iSndKmKeyLen; //Key length @@ -192,7 +196,7 @@ class CCryptoControl return false; } - CCryptoControl(CUDT* parent, SRTSOCKET id); + CCryptoControl(srt::CUDT* parent, SRTSOCKET id); // DEBUG PURPOSES: std::string CONID() const; @@ -254,14 +258,14 @@ class CCryptoControl /// the encryption will fail. /// XXX Encryption flags in the PH_MSGNO /// field in the header must be correctly set before calling. - EncryptionStatus encrypt(CPacket& w_packet); + EncryptionStatus encrypt(srt::CPacket& w_packet); /// Decrypts the packet. If the packet has ENCKEYSPEC part /// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts /// only if the encryption correctly configured, otherwise it /// fails. After successful decryption, the ENCKEYSPEC part // in PH_MSGNO is set to EK_NOENC. - EncryptionStatus decrypt(CPacket& w_packet); + EncryptionStatus decrypt(srt::CPacket& w_packet); ~CCryptoControl(); }; diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 3786137d5..63533855c 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -348,11 +348,18 @@ std::string DisplayEpollWatch(); } }; +namespace srt +{ + class CUDT; + class CRendezvousQueue; + class CUDTGroup; +} + class CEPoll { -friend class CUDT; -friend class CUDTGroup; -friend class CRendezvousQueue; +friend class srt::CUDT; +friend class srt::CUDTGroup; +friend class srt::CRendezvousQueue; public: CEPoll(); diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index c02af2821..31239eed8 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -35,6 +35,7 @@ using namespace std; using namespace srt_logging; +namespace srt { const char FECFilterBuiltin::defaultConfig [] = "fec,rows:1,layout:staircase,arq:onreq"; @@ -1118,11 +1119,11 @@ static void DebugPrintCells(int32_t base, const std::deque& cells, size_t } // Ok, we have some empty cells, so just adjust to the start of a row. - size_t bstep = i % row_size; - if (i < bstep) // you never know... - i = 0; - else - i -= bstep; + size_t bstep = i % row_size; + if (i < bstep) // you never know... + i = 0; + else + i -= bstep; for ( ; i < cells.size(); i += row_size ) { @@ -2557,3 +2558,5 @@ size_t FECFilterBuiltin::ExtendColumns(size_t colgx) return colgx; } + +} // namespace srt diff --git a/srtcore/fec.h b/srtcore/fec.h index 9423f0d35..57305bfac 100644 --- a/srtcore/fec.h +++ b/srtcore/fec.h @@ -19,6 +19,8 @@ #include "packetfilter_api.h" +namespace srt { + class FECFilterBuiltin: public SrtPacketFilterBase { SrtFilterConfig cfg; @@ -270,4 +272,6 @@ class FECFilterBuiltin: public SrtPacketFilterBase static bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg); }; +} // namespace srt + #endif diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b16bfc349..854508ef4 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -13,6 +13,8 @@ using namespace srt_logging; // The SRT_DEF_VERSION is defined in core.cpp. extern const int32_t SRT_DEF_VERSION; +namespace srt { + int32_t CUDTGroup::s_tokenGen = 0; // [[using locked(this->m_GroupLock)]]; @@ -4587,3 +4589,5 @@ void CUDTGroup::debugGroup() } } #endif + +} // namespace srt diff --git a/srtcore/group.h b/srtcore/group.h index ad779e39f..ec5d124d3 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -22,20 +22,24 @@ Written by #include "group_common.h" #include "group_backup.h" +namespace srt +{ + #if ENABLE_HEAVY_LOGGING const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"}; #endif + class CUDTGroup { friend class CUDTUnited; - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; - typedef srt::sync::steady_clock steady_clock; - typedef srt::groups::SocketData SocketData; - typedef srt::groups::SendBackupCtx SendBackupCtx; - typedef srt::groups::BackupMemberState BackupMemberState; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + typedef sync::steady_clock steady_clock; + typedef groups::SocketData SocketData; + typedef groups::SendBackupCtx SendBackupCtx; + typedef groups::BackupMemberState BackupMemberState; public: typedef SRT_MEMBERSTATUS GroupState; @@ -97,7 +101,7 @@ class CUDTGroup typedef std::list group_t; typedef group_t::iterator gli_t; - typedef std::vector< std::pair > sendable_t; + typedef std::vector< std::pair > sendable_t; struct Sendstate { @@ -211,7 +215,7 @@ class CUDTGroup private: // For Backup, sending all previous packet - int sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc); + int sendBackupRexmit(srt::CUDT& core, SRT_MSGCTRL& w_mc); // Support functions for sendBackup and sendBroadcast /// Check if group member is idle. @@ -232,7 +236,7 @@ class CUDTGroup /// @param[in] currtime current timestamp void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime); - void sendBackup_AssignBackupState(CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); + void sendBackup_AssignBackupState(srt::CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime); /// Qualify the state of the active link: fresh, stable, unstable, wary. /// @retval active backup member state: fresh, stable, unstable, wary. @@ -319,7 +323,7 @@ class CUDTGroup void setOpt(SRT_SOCKOPT optname, const void* optval, int optlen); void getOpt(SRT_SOCKOPT optName, void* optval, int& w_optlen); - void deriveSettings(CUDT* source); + void deriveSettings(srt::CUDT* source); bool applyFlags(uint32_t flags, HandshakeSide); SRT_SOCKSTATUS getStatus(); @@ -333,14 +337,14 @@ class CUDTGroup return m_type == SRT_GTYPE_BROADCAST; } - srt::sync::Mutex* exp_groupLock() { return &m_GroupLock; } - void addEPoll(int eid); - void removeEPollEvents(const int eid); - void removeEPollID(const int eid); - void updateReadState(SRTSOCKET sock, int32_t sequence); - void updateWriteState(); - void updateFailedLink(); - void activateUpdateEvent(bool still_have_items); + sync::Mutex* exp_groupLock() { return &m_GroupLock; } + void addEPoll(int eid); + void removeEPollEvents(const int eid); + void removeEPollID(const int eid); + void updateReadState(SRTSOCKET sock, int32_t sequence); + void updateWriteState(); + void updateFailedLink(); + void activateUpdateEvent(bool still_have_items); /// Update the in-group array of packet providers per sequence number. /// Also basing on the information already provided by possibly other sockets, @@ -353,16 +357,16 @@ class CUDTGroup /// @param provider The core of the socket for which the packet was dispatched /// @param time TSBPD time of this packet /// @return The bitmap that marks by 'false' packets lost since next to exp_sequence - std::vector providePacket(int32_t exp_sequence, int32_t sequence, CUDT* provider, uint64_t time); + std::vector providePacket(int32_t exp_sequence, int32_t sequence, srt::CUDT* provider, uint64_t time); /// This is called from the ACK action by particular socket, which /// actually signs off the packet for extraction. /// /// @param core The socket core for which the ACK was sent /// @param ack The past-the-last-received ACK sequence number - void readyPackets(CUDT* core, int32_t ack); + void readyPackets(srt::CUDT* core, int32_t ack); - void syncWithSocket(const CUDT& core, const HandshakeSide side); + void syncWithSocket(const srt::CUDT& core, const HandshakeSide side); int getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize); int getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize); int configure(const char* str); @@ -390,7 +394,7 @@ class CUDTGroup // If so, grab the status of all member sockets. void getGroupCount(size_t& w_size, bool& w_still_alive); - class CUDTUnited* m_pGlobal; + class srt::CUDTUnited* m_pGlobal; srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; @@ -493,7 +497,7 @@ class CUDTGroup bool isStillBusy() { - srt::sync::ScopedLock glk(m_GroupLock); + sync::ScopedLock glk(m_GroupLock); return m_iBusy || !m_Group.empty(); } @@ -647,7 +651,7 @@ class CUDTGroup ReadPos* checkPacketAhead(); - void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); + void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); /// The function polls alive member sockets and retrieves a list of read-ready. /// [acquires lock for CUDT::s_UDTUnited.m_GlobControlLock] @@ -656,7 +660,7 @@ class CUDTGroup /// @returns list of read-ready sockets /// @throws CUDTException(MJ_CONNECTION, MN_NOCONN, 0) /// @throws CUDTException(MJ_AGAIN, MN_RDAVAIL, 0) - std::vector recv_WaitForReadReady(const std::vector& aliveMembers, std::set& w_broken); + std::vector recv_WaitForReadReady(const std::vector& aliveMembers, std::set& w_broken); // This is the sequence number of a packet that has been previously // delivered. Initially it should be set to SRT_SEQNO_NONE so that the sequence read @@ -791,11 +795,11 @@ class CUDTGroup } // Live state synchronization - bool getBufferTimeBase(CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); + bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn); - void synchronizeDrift(CUDT* cu, duration udrift, time_point newtimebase); + void synchronizeDrift(srt::CUDT* cu, duration udrift, time_point newtimebase); - void updateLatestRcv(CUDTSocket*); + void updateLatestRcv(srt::CUDTSocket*); // Property accessors SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID); @@ -809,4 +813,6 @@ class CUDTGroup SRTU_PROPERTY_RO(bool, closing, m_bClosing); }; +} // namespace srt + #endif // INC_SRT_GROUP_H diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index 95f9cb899..755fb9dbf 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -58,18 +58,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "utilities.h" using namespace std; - - -CHandShake::CHandShake(): -m_iVersion(0), -m_iType(0), // Universal: UDT_UNDEFINED or no flags -m_iISN(0), -m_iMSS(0), -m_iFlightFlagSize(0), -m_iReqType(URQ_WAVEAHAND), -m_iID(0), -m_iCookie(0), -m_extension(false) +using namespace srt; + + +CHandShake::CHandShake() + : m_iVersion(0) + , m_iType(0) // Universal: UDT_UNDEFINED or no flags + , m_iISN(0) + , m_iMSS(0) + , m_iFlightFlagSize(0) + , m_iReqType(URQ_WAVEAHAND) + , m_iID(0) + , m_iCookie(0) + , m_extension(false) { for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = 0; diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 4524d2232..26a4b2080 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -174,14 +174,14 @@ namespace srt_logging using namespace srt_logging; // Set up the aliases in the constructure -CPacket::CPacket(): -m_extra_pad(), -m_data_owned(false), -m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])), -m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])), -m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])), -m_iID((int32_t&)(m_nHeader[SRT_PH_ID])), -m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) +srt::CPacket::CPacket(): + m_extra_pad(), + m_data_owned(false), + m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])), + m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])), + m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])), + m_iID((int32_t&)(m_nHeader[SRT_PH_ID])), + m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -195,12 +195,12 @@ m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) m_PacketVector[PV_DATA].set(NULL, 0); } -char* CPacket::getData() +char* srt::CPacket::getData() { return (char*)m_PacketVector[PV_DATA].dataRef(); } -void CPacket::allocate(size_t alloc_buffer_size) +void srt::CPacket::allocate(size_t alloc_buffer_size) { if (m_data_owned) { @@ -214,14 +214,14 @@ void CPacket::allocate(size_t alloc_buffer_size) m_data_owned = true; } -void CPacket::deallocate() +void srt::CPacket::deallocate() { if (m_data_owned) delete [] (char*)m_PacketVector[PV_DATA].data(); m_PacketVector[PV_DATA].set(NULL, 0); } -char* CPacket::release() +char* srt::CPacket::release() { // When not owned, release returns NULL. char* buffer = NULL; @@ -235,7 +235,7 @@ char* CPacket::release() return buffer; } -CPacket::~CPacket() +srt::CPacket::~CPacket() { // PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer. // Delete the internal buffer only if it was declared as owned. @@ -244,17 +244,17 @@ CPacket::~CPacket() } -size_t CPacket::getLength() const +size_t srt::CPacket::getLength() const { return m_PacketVector[PV_DATA].size(); } -void CPacket::setLength(size_t len) +void srt::CPacket::setLength(size_t len) { m_PacketVector[PV_DATA].setLength(len); } -void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); @@ -365,7 +365,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } } -void CPacket::toNL() +void srt::CPacket::toNL() { // XXX USE HtoNLA! if (isControl()) @@ -383,7 +383,7 @@ void CPacket::toNL() } } -void CPacket::toHL() +void srt::CPacket::toHL() { // convert back into local host order uint32_t* p = m_nHeader; @@ -401,22 +401,22 @@ void CPacket::toHL() } -IOVector* CPacket::getPacketVector() +IOVector* srt::CPacket::getPacketVector() { return m_PacketVector; } -UDTMessageType CPacket::getType() const +UDTMessageType srt::CPacket::getType() const { return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO])); } -int CPacket::getExtendedType() const +int srt::CPacket::getExtendedType() const { return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -int32_t CPacket::getAckSeqNo() const +int32_t srt::CPacket::getAckSeqNo() const { // read additional information field // This field is used only in UMSG_ACK and UMSG_ACKACK, @@ -425,7 +425,7 @@ int32_t CPacket::getAckSeqNo() const return m_nHeader[SRT_PH_MSGNO]; } -uint16_t CPacket::getControlFlags() const +uint16_t srt::CPacket::getControlFlags() const { // This returns exactly the "extended type" value, // which is not used at all in case when the standard @@ -434,17 +434,17 @@ uint16_t CPacket::getControlFlags() const return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]); } -PacketBoundary CPacket::getMsgBoundary() const +PacketBoundary srt::CPacket::getMsgBoundary() const { return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO])); } -bool CPacket::getMsgOrderFlag() const +bool srt::CPacket::getMsgOrderFlag() const { return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]); } -int32_t CPacket::getMsgSeq(bool has_rexmit) const +int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const { if ( has_rexmit ) { @@ -456,13 +456,13 @@ int32_t CPacket::getMsgSeq(bool has_rexmit) const } } -bool CPacket::getRexmitFlag() const +bool srt::CPacket::getRexmitFlag() const { // return false; // return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]); } -EncryptionKeySpec CPacket::getMsgCryptoFlags() const +EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const { return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO])); } @@ -470,19 +470,19 @@ EncryptionKeySpec CPacket::getMsgCryptoFlags() const // This is required as the encryption/decryption happens in place. // This is required to clear off the flags after decryption or set // crypto flags after encrypting a packet. -void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) +void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec) { int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask; m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec); } -uint32_t CPacket::getMsgTimeStamp() const +uint32_t srt::CPacket::getMsgTimeStamp() const { // SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK; } -CPacket* CPacket::clone() const +srt::CPacket* srt::CPacket::clone() const { CPacket* pkt = new CPacket; memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE); @@ -493,6 +493,8 @@ CPacket* CPacket::clone() const return pkt; } +namespace srt { + // Useful for debugging std::string PacketMessageFlagStr(uint32_t msgno_field) { @@ -521,8 +523,10 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) os << val; } +} // namespace srt + #if ENABLE_LOGGING -std::string CPacket::Info() +std::string srt::CPacket::Info() { std::ostringstream os; os << "TARGET=@" << m_iID << " "; diff --git a/srtcore/packet.h b/srtcore/packet.h index 8724e9d07..c968b08c0 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -214,9 +214,9 @@ inline EncryptionKeySpec GetEncryptionKeySpec(int32_t msgno) const int32_t PUMASK_SEQNO_PROBE = 0xF; -std::string PacketMessageFlagStr(uint32_t msgno_field); -class CChannel; +namespace srt { +std::string PacketMessageFlagStr(uint32_t msgno_field); class CPacket { @@ -284,7 +284,7 @@ friend class CRcvQueue; void setControl(UDTMessageType type) { - m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); + m_nHeader[srt::SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); } /// Read the extended packet type. @@ -430,4 +430,6 @@ friend class CRcvQueue; #endif }; +} // namespace srt + #endif diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1122c06a2..dffb2a8ba 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace srt_logging; using namespace srt::sync; -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) +bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf) { if (!SrtParseConfig(s, (w_config))) return false; @@ -43,13 +43,13 @@ bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config, PacketFilter::F return true; } -bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config) +bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config) { return ParseFilterConfig(s, (w_config), NULL); } // Parameters are passed by value because they need to be potentially modicied inside. -bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) +bool srt::CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) { PacketFilter::Factory* fac = PacketFilter::find(w_agent.type); if (!fac) @@ -109,18 +109,20 @@ bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer) return true; } -struct SortBySequence -{ - bool operator()(const CUnit* u1, const CUnit* u2) +namespace srt { + struct SortBySequence { - int32_t s1 = u1->m_Packet.getSeqNo(); - int32_t s2 = u2->m_Packet.getSeqNo(); + bool operator()(const CUnit* u1, const CUnit* u2) + { + int32_t s1 = u1->m_Packet.getSeqNo(); + int32_t s2 = u2->m_Packet.getSeqNo(); - return CSeqNo::seqcmp(s1, s2) < 0; - } -}; + return CSeqNo::seqcmp(s1, s2) < 0; + } + }; +} // namespace srt -void PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_seqs_t& w_loss_seqs) +void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_seqs_t& w_loss_seqs) { const CPacket& rpkt = unit->m_Packet; @@ -206,7 +208,7 @@ void PacketFilter::receive(CUnit* unit, std::vector& w_incoming, loss_se } -bool PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) +bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) { bool have = m_filter->packControlPacket(m_sndctlpkt, seq); if (!have) @@ -238,7 +240,7 @@ bool PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet) } -void PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) +void srt::PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) { if (m_provided.empty()) return; @@ -273,19 +275,21 @@ void PacketFilter::InsertRebuilt(vector& incoming, CUnitQueue* uq) m_provided.clear(); } -bool PacketFilter::IsBuiltin(const string& s) +bool srt::PacketFilter::IsBuiltin(const string& s) { return builtin_filters.count(s); } +namespace srt { std::set PacketFilter::builtin_filters; PacketFilter::filters_map_t PacketFilter::filters; +} -PacketFilter::Factory::~Factory() +srt::PacketFilter::Factory::~Factory() { } -void PacketFilter::globalInit() +void srt::PacketFilter::globalInit() { // Add here builtin packet filters and mark them // as builtin. This will disallow users to register @@ -295,7 +299,7 @@ void PacketFilter::globalInit() builtin_filters.insert("fec"); } -bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr) +bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr) { m_parent = parent; @@ -329,7 +333,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co return true; } -bool PacketFilter::correctConfig(const SrtFilterConfig& conf) +bool srt::PacketFilter::correctConfig(const SrtFilterConfig& conf) { const string* pname = map_getp(conf.parameters, "type"); @@ -346,7 +350,7 @@ bool PacketFilter::correctConfig(const SrtFilterConfig& conf) return true; } -PacketFilter::~PacketFilter() +srt::PacketFilter::~PacketFilter() { delete m_filter; } diff --git a/srtcore/packetfilter.h b/srtcore/packetfilter.h index 545e38e02..a26b07fa5 100644 --- a/srtcore/packetfilter.h +++ b/srtcore/packetfilter.h @@ -19,6 +19,8 @@ #include "utilities.h" #include "packetfilter_api.h" +namespace srt { + class CUnitQueue; struct CUnit; class CUDT; @@ -212,4 +214,6 @@ inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_fi bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf); +} // namespace srt + #endif diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 74279f9e3..d714b865b 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -19,6 +19,8 @@ #include #include +namespace srt { + class CPacket; enum SrtPktHeaderFields @@ -151,6 +153,6 @@ class SrtPacketFilterBase } }; - +} // namespace srt #endif diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index ccc14b9b8..fe323ab4c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -65,7 +65,7 @@ using namespace std; using namespace srt::sync; using namespace srt_logging; -CUnitQueue::CUnitQueue() +srt::CUnitQueue::CUnitQueue() : m_pQEntry(NULL) , m_pCurrQueue(NULL) , m_pLastQueue(NULL) @@ -76,7 +76,7 @@ CUnitQueue::CUnitQueue() { } -CUnitQueue::~CUnitQueue() +srt::CUnitQueue::~CUnitQueue() { CQEntry* p = m_pQEntry; @@ -94,7 +94,7 @@ CUnitQueue::~CUnitQueue() } } -int CUnitQueue::init(int size, int mss, int version) +int srt::CUnitQueue::init(int size, int mss, int version) { CQEntry* tempq = NULL; CUnit* tempu = NULL; @@ -138,7 +138,7 @@ int CUnitQueue::init(int size, int mss, int version) // XXX Lots of common code with CUnitQueue:init. // Consider merging. -int CUnitQueue::increase() +int srt::CUnitQueue::increase() { // adjust/correct m_iCount int real_count = 0; @@ -202,13 +202,13 @@ int CUnitQueue::increase() return 0; } -int CUnitQueue::shrink() +int srt::CUnitQueue::shrink() { // currently queue cannot be shrunk. return -1; } -CUnit* CUnitQueue::getNextAvailUnit() +srt::CUnit* srt::CUnitQueue::getNextAvailUnit() { if (m_iCount * 10 > m_iSize * 9) increase(); @@ -237,7 +237,7 @@ CUnit* CUnitQueue::getNextAvailUnit() return NULL; } -void CUnitQueue::makeUnitFree(CUnit* unit) +void srt::CUnitQueue::makeUnitFree(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); @@ -245,7 +245,7 @@ void CUnitQueue::makeUnitFree(CUnit* unit) --m_iCount; } -void CUnitQueue::makeUnitGood(CUnit* unit) +void srt::CUnitQueue::makeUnitGood(CUnit* unit) { SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); @@ -253,7 +253,7 @@ void CUnitQueue::makeUnitGood(CUnit* unit) ++m_iCount; } -CSndUList::CSndUList() +srt::CSndUList::CSndUList() : m_pHeap(NULL) , m_iArrayLength(512) , m_iLastEntry(-1) @@ -265,12 +265,12 @@ CSndUList::CSndUList() m_pHeap = new CSNode*[m_iArrayLength]; } -CSndUList::~CSndUList() +srt::CSndUList::~CSndUList() { delete[] m_pHeap; } -void CSndUList::update(const CUDT* u, EReschedule reschedule) +void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) { ScopedLock listguard(m_ListLock); @@ -296,7 +296,7 @@ void CSndUList::update(const CUDT* u, EReschedule reschedule) insert_(steady_clock::now(), u); } -int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) +int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) { ScopedLock listguard(m_ListLock); @@ -337,14 +337,14 @@ int CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) return 1; } -void CSndUList::remove(const CUDT* u) +void srt::CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); remove_(u); } -steady_clock::time_point CSndUList::getNextProcTime() +steady_clock::time_point srt::CSndUList::getNextProcTime() { ScopedLock listguard(m_ListLock); @@ -354,7 +354,7 @@ steady_clock::time_point CSndUList::getNextProcTime() return m_pHeap[0]->m_tsTimeStamp; } -void CSndUList::realloc_() +void srt::CSndUList::realloc_() { CSNode** temp = NULL; @@ -373,7 +373,7 @@ void CSndUList::realloc_() m_pHeap = temp; } -void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) +void srt::CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) { // increase the heap array size if necessary if (m_iLastEntry == m_iArrayLength - 1) @@ -382,7 +382,7 @@ void CSndUList::insert_(const steady_clock::time_point& ts, const CUDT* u) insert_norealloc_(ts, u); } -void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) +void srt::CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT* u) { CSNode* n = u->m_pSNode; @@ -422,7 +422,7 @@ void CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const CUDT } } -void CSndUList::remove_(const CUDT* u) +void srt::CSndUList::remove_(const CUDT* u) { CSNode* n = u->m_pSNode; @@ -462,7 +462,7 @@ void CSndUList::remove_(const CUDT* u) } // -CSndQueue::CSndQueue() +srt::CSndQueue::CSndQueue() : m_pSndUList(NULL) , m_pChannel(NULL) , m_pTimer(NULL) @@ -472,7 +472,7 @@ CSndQueue::CSndQueue() setupCond(m_WindowCond, "Window"); } -CSndQueue::~CSndQueue() +srt::CSndQueue::~CSndQueue() { m_bClosing = true; @@ -493,20 +493,20 @@ CSndQueue::~CSndQueue() delete m_pSndUList; } -int CSndQueue::ioctlQuery(int type) const +int srt::CSndQueue::ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } -int CSndQueue::sockoptQuery(int level, int type) const +int srt::CSndQueue::sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } #if ENABLE_LOGGING -int CSndQueue::m_counter = 0; +int srt::CSndQueue::m_counter = 0; #endif -void CSndQueue::init(CChannel* c, CTimer* t) +void srt::CSndQueue::init(CChannel* c, CTimer* t) { m_pChannel = c; m_pTimer = t; @@ -526,24 +526,24 @@ void CSndQueue::init(CChannel* c, CTimer* t) throw CUDTException(MJ_SYSTEMRES, MN_THREAD); } -int CSndQueue::getIpTTL() const +int srt::CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : -1; } -int CSndQueue::getIpToS() const +int srt::CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } #ifdef SRT_ENABLE_BINDTODEVICE -bool CSndQueue::getBind(char* dst, size_t len) const +bool srt::CSndQueue::getBind(char* dst, size_t len) const { return m_pChannel ? m_pChannel->getBind(dst, len) : false; } #endif -void* CSndQueue::worker(void* param) +void* srt::CSndQueue::worker(void* param) { CSndQueue* self = (CSndQueue*)param; @@ -645,7 +645,7 @@ void* CSndQueue::worker(void* param) return NULL; } -int CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) +int srt::CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) { // send out the packet immediately (high priority), this is a control packet m_pChannel->sendto(w_addr, w_packet); @@ -653,15 +653,15 @@ int CSndQueue::sendto(const sockaddr_any& w_addr, CPacket& w_packet) } // -CRcvUList::CRcvUList() +srt::CRcvUList::CRcvUList() : m_pUList(NULL) , m_pLast(NULL) { } -CRcvUList::~CRcvUList() {} +srt::CRcvUList::~CRcvUList() {} -void CRcvUList::insert(const CUDT* u) +void srt::CRcvUList::insert(const CUDT* u) { CRNode* n = u->m_pRNode; n->m_tsTimeStamp = steady_clock::now(); @@ -682,7 +682,7 @@ void CRcvUList::insert(const CUDT* u) m_pLast = n; } -void CRcvUList::remove(const CUDT* u) +void srt::CRcvUList::remove(const CUDT* u) { CRNode* n = u->m_pRNode; @@ -713,7 +713,7 @@ void CRcvUList::remove(const CUDT* u) n->m_pNext = n->m_pPrev = NULL; } -void CRcvUList::update(const CUDT* u) +void srt::CRcvUList::update(const CUDT* u) { CRNode* n = u->m_pRNode; @@ -744,13 +744,13 @@ void CRcvUList::update(const CUDT* u) } // -CHash::CHash() +srt::CHash::CHash() : m_pBucket(NULL) , m_iHashSize(0) { } -CHash::~CHash() +srt::CHash::~CHash() { for (int i = 0; i < m_iHashSize; ++i) { @@ -766,7 +766,7 @@ CHash::~CHash() delete[] m_pBucket; } -void CHash::init(int size) +void srt::CHash::init(int size) { m_pBucket = new CBucket*[size]; @@ -776,7 +776,7 @@ void CHash::init(int size) m_iHashSize = size; } -CUDT* CHash::lookup(int32_t id) +srt::CUDT* srt::CHash::lookup(int32_t id) { // simple hash function (% hash table size); suitable for socket descriptors CBucket* b = m_pBucket[id % m_iHashSize]; @@ -791,7 +791,7 @@ CUDT* CHash::lookup(int32_t id) return NULL; } -void CHash::insert(int32_t id, CUDT* u) +void srt::CHash::insert(int32_t id, CUDT* u) { CBucket* b = m_pBucket[id % m_iHashSize]; @@ -803,7 +803,7 @@ void CHash::insert(int32_t id, CUDT* u) m_pBucket[id % m_iHashSize] = n; } -void CHash::remove(int32_t id) +void srt::CHash::remove(int32_t id) { CBucket* b = m_pBucket[id % m_iHashSize]; CBucket* p = NULL; @@ -828,18 +828,18 @@ void CHash::remove(int32_t id) } // -CRendezvousQueue::CRendezvousQueue() +srt::CRendezvousQueue::CRendezvousQueue() : m_lRendezvousID() , m_RIDListLock() { } -CRendezvousQueue::~CRendezvousQueue() +srt::CRendezvousQueue::~CRendezvousQueue() { m_lRendezvousID.clear(); } -void CRendezvousQueue::insert(const SRTSOCKET& id, +void srt::CRendezvousQueue::insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) @@ -858,7 +858,7 @@ void CRendezvousQueue::insert(const SRTSOCKET& id, << " (total connectors: " << m_lRendezvousID.size() << ")"); } -void CRendezvousQueue::remove(const SRTSOCKET& id) +void srt::CRendezvousQueue::remove(const SRTSOCKET& id) { ScopedLock lkv(m_RIDListLock); @@ -872,7 +872,7 @@ void CRendezvousQueue::remove(const SRTSOCKET& id) } } -CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const +srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) const { ScopedLock vg(m_RIDListLock); @@ -903,7 +903,7 @@ CUDT* CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& w_id) cons return NULL; } -void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) +void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) { vector toRemove, toProcess; @@ -1005,7 +1005,7 @@ void CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, con } } -bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, +bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, int iDstSockID, vector& toRemove, @@ -1109,7 +1109,7 @@ bool CRendezvousQueue::qualifyToHandle(EReadStatus rst, } // -CRcvQueue::CRcvQueue() +srt::CRcvQueue::CRcvQueue() : m_WorkerThread() , m_UnitQueue() , m_pRcvUList(NULL) @@ -1129,7 +1129,7 @@ CRcvQueue::CRcvQueue() setupCond(m_BufferCond, "QueueBuffer"); } -CRcvQueue::~CRcvQueue() +srt::CRcvQueue::~CRcvQueue() { m_bClosing = true; @@ -1158,10 +1158,10 @@ CRcvQueue::~CRcvQueue() } #if ENABLE_LOGGING -int CRcvQueue::m_counter = 0; +int srt::CRcvQueue::m_counter = 0; #endif -void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) +void srt::CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel* cc, CTimer* t) { m_szPayloadSize = payload; @@ -1189,7 +1189,7 @@ void CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CChannel } } -void* CRcvQueue::worker(void* param) +void* srt::CRcvQueue::worker(void* param) { CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->m_UnitQueue.getIPversion()); @@ -1322,7 +1322,7 @@ void* CRcvQueue::worker(void* param) return NULL; } -EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) +EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) { #if !USE_BUSY_WAITING // This might be not really necessary, and probably @@ -1380,7 +1380,7 @@ EReadStatus CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockad return rst; } -EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& addr) { HLOGC(cnlog.Debug, log << "Got sockID=0 from " << addr.str() << " - trying to resolve it as a connection request..."); @@ -1423,7 +1423,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. } -EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); if (!u) @@ -1475,7 +1475,7 @@ EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, // This function then tries to manage the packet as a rendezvous connection // request in ASYNC mode; when this is not applicable, it stores the packet // in the "receiving queue" so that it will be picked up in the "main" thread. -EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) +EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) { // This 'retrieve' requires that 'id' be either one of those // stored in the rendezvous queue (see CRcvQueue::registerConnector) @@ -1605,7 +1605,7 @@ EConnectStatus CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, c return CONN_CONTINUE; } -void CRcvQueue::stopWorker() +void srt::CRcvQueue::stopWorker() { // We use the decent way, so we say to the thread "please exit". m_bClosing = true; @@ -1622,7 +1622,7 @@ void CRcvQueue::stopWorker() m_WorkerThread.join(); } -int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) +int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) { UniqueLock bufferlock(m_BufferLock); CSync buffercond(m_BufferCond, bufferlock); @@ -1676,7 +1676,7 @@ int CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) return (int)w_packet.getLength(); } -int CRcvQueue::setListener(CUDT* u) +int srt::CRcvQueue::setListener(CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1687,7 +1687,7 @@ int CRcvQueue::setListener(CUDT* u) return 0; } -void CRcvQueue::removeListener(const CUDT* u) +void srt::CRcvQueue::removeListener(const CUDT* u) { ScopedLock lslock(m_LSLock); @@ -1695,7 +1695,7 @@ void CRcvQueue::removeListener(const CUDT* u) m_pListener = NULL; } -void CRcvQueue::registerConnector(const SRTSOCKET& id, +void srt::CRcvQueue::registerConnector(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const steady_clock::time_point& ttl) @@ -1705,7 +1705,7 @@ void CRcvQueue::registerConnector(const SRTSOCKET& id, m_pRendezvousQueue->insert(id, u, addr, ttl); } -void CRcvQueue::removeConnector(const SRTSOCKET& id) +void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) { HLOGC(cnlog.Debug, log << "removeConnector: removing @" << id); m_pRendezvousQueue->remove(id); @@ -1727,19 +1727,19 @@ void CRcvQueue::removeConnector(const SRTSOCKET& id) } } -void CRcvQueue::setNewEntry(CUDT* u) +void srt::CRcvQueue::setNewEntry(CUDT* u) { HLOGC(cnlog.Debug, log << CUDTUnited::CONID(u->m_SocketID) << "setting socket PENDING FOR CONNECTION"); ScopedLock listguard(m_IDLock); m_vNewEntry.push_back(u); } -bool CRcvQueue::ifNewEntry() +bool srt::CRcvQueue::ifNewEntry() { return !(m_vNewEntry.empty()); } -CUDT* CRcvQueue::getNewEntry() +srt::CUDT* srt::CRcvQueue::getNewEntry() { ScopedLock listguard(m_IDLock); @@ -1752,7 +1752,7 @@ CUDT* CRcvQueue::getNewEntry() return u; } -void CRcvQueue::storePkt(int32_t id, CPacket* pkt) +void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) { UniqueLock bufferlock(m_BufferLock); CSync passcond(m_BufferCond, bufferlock); @@ -1774,7 +1774,7 @@ void CRcvQueue::storePkt(int32_t id, CPacket* pkt) } } -void CMultiplexer::destroy() +void srt::CMultiplexer::destroy() { // Reverse order of the assigned delete m_pRcvQueue; diff --git a/srtcore/queue.h b/srtcore/queue.h index a412fd516..b2ccb36d8 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -63,8 +63,10 @@ modified by #include #include -class CUDT; +namespace srt +{ class CChannel; +class CUDT; struct CUnit { @@ -92,7 +94,6 @@ class CUnitQueue /// @param [in] mss maximum segment size /// @param [in] version IP version /// @return 0: success, -1: failure. - int init(int size, int mss, int version); /// Increase (double) the unit queue size. @@ -112,7 +113,6 @@ class CUnitQueue public: // Operations on units /// find an available unit for incoming packet. /// @return Pointer to the available unit, NULL if not found. - CUnit* getNextAvailUnit(); void makeUnitFree(CUnit* unit); @@ -149,8 +149,8 @@ class CUnitQueue struct CSNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + sync::steady_clock::time_point m_tsTimeStamp; int m_iHeapLoc; // location on the heap, -1 means not on the heap }; @@ -193,7 +193,7 @@ class CSndUList /// Retrieve the next scheduled processing time. /// @return Scheduled processing time of the first UDT socket in the list. - srt::sync::steady_clock::time_point getNextProcTime(); + sync::steady_clock::time_point getNextProcTime(); private: /// Doubles the size of the list. @@ -204,14 +204,14 @@ class CSndUList /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); + void insert_(const sync::steady_clock::time_point& ts, const CUDT* u); /// Insert a new UDT instance into the list without realloc. /// Should be called if there is a gauranteed space for the element. /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const srt::sync::steady_clock::time_point& ts, const CUDT* u); + void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u); void remove_(const CUDT* u); @@ -220,12 +220,12 @@ class CSndUList int m_iArrayLength; // physical length of the array int m_iLastEntry; // position of last entry on the heap array - srt::sync::Mutex m_ListLock; + sync::Mutex m_ListLock; - srt::sync::Mutex* m_pWindowLock; - srt::sync::Condition* m_pWindowCond; + sync::Mutex* m_pWindowLock; + sync::Condition* m_pWindowCond; - srt::sync::CTimer* m_pTimer; + sync::CTimer* m_pTimer; private: CSndUList(const CSndUList&); @@ -234,8 +234,8 @@ class CSndUList struct CRNode { - CUDT* m_pUDT; // Pointer to the instance of CUDT socket - srt::sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp + CUDT* m_pUDT; // Pointer to the instance of CUDT socket + sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp CRNode* m_pPrev; // previous link CRNode* m_pNext; // next link @@ -401,7 +401,7 @@ class CRendezvousQueue }; std::list m_lRendezvousID; // The sockets currently in rendezvous mode - mutable srt::sync::Mutex m_RIDListLock; + mutable sync::Mutex m_RIDListLock; }; class CSndQueue @@ -514,7 +514,7 @@ class CRcvQueue /// @param [in] c UDP channel to be associated to the queue /// @param [in] t timer - void init(int size, size_t payload, int version, int hsize, CChannel* c, srt::sync::CTimer* t); + void init(int size, size_t payload, int version, int hsize, CChannel* c, sync::CTimer* t); /// Read a packet for a specific UDT socket id. /// @param [in] id Socket ID @@ -528,8 +528,8 @@ class CRcvQueue void setClosing() { m_bClosing = true; } private: - static void* worker(void* param); - srt::sync::CThread m_WorkerThread; + static void* worker(void* param); + sync::CThread m_WorkerThread; // Subroutines of worker EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); @@ -537,11 +537,11 @@ class CRcvQueue EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); private: - CUnitQueue m_UnitQueue; // The received packet queue - CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue - CHash* m_pHash; // Hash table for UDT socket looking up - CChannel* m_pChannel; // UDP channel for receving packets - srt::sync::CTimer* m_pTimer; // shared timer with the snd queue + CUnitQueue m_UnitQueue; // The received packet queue + CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue + CHash* m_pHash; // Hash table for UDT socket looking up + CChannel* m_pChannel; // UDP channel for receving packets + sync::CTimer* m_pTimer; // shared timer with the snd queue size_t m_szPayloadSize; // packet payload size @@ -554,10 +554,10 @@ class CRcvQueue int setListener(CUDT* u); void removeListener(const CUDT* u); - void registerConnector(const SRTSOCKET& id, - CUDT* u, - const sockaddr_any& addr, - const srt::sync::steady_clock::time_point& ttl); + void registerConnector(const SRTSOCKET& id, + CUDT* u, + const sockaddr_any& addr, + const sync::steady_clock::time_point& ttl); void removeConnector(const SRTSOCKET& id); void setNewEntry(CUDT* u); @@ -567,16 +567,16 @@ class CRcvQueue void storePkt(int32_t id, CPacket* pkt); private: - srt::sync::Mutex m_LSLock; + sync::Mutex m_LSLock; CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode std::vector m_vNewEntry; // newly added entries, to be inserted - srt::sync::Mutex m_IDLock; + sync::Mutex m_IDLock; std::map > m_mBuffer; // temporary buffer for rendezvous connection request - srt::sync::Mutex m_BufferLock; - srt::sync::Condition m_BufferCond; + sync::Mutex m_BufferLock; + sync::Condition m_BufferCond; private: CRcvQueue(const CRcvQueue&); @@ -585,10 +585,10 @@ class CRcvQueue struct CMultiplexer { - CSndQueue* m_pSndQueue; // The sending queue - CRcvQueue* m_pRcvQueue; // The receiving queue - CChannel* m_pChannel; // The UDP channel for sending and receiving - srt::sync::CTimer* m_pTimer; // The timer + CSndQueue* m_pSndQueue; // The sending queue + CRcvQueue* m_pRcvQueue; // The receiving queue + CChannel* m_pChannel; // The UDP channel for sending and receiving + sync::CTimer* m_pTimer; // The timer int m_iPort; // The UDP port number of this multiplexer int m_iIPversion; // Address family (AF_INET or AF_INET6) @@ -611,4 +611,6 @@ struct CMultiplexer void destroy(); }; +} // namespace srt + #endif diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 1db4bb63a..72b90195e 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -396,7 +396,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iMSS = ival; @@ -435,7 +435,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::UDP_HDR_SIZE); + co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); } }; @@ -449,7 +449,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; + const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; @@ -937,8 +937,8 @@ struct CSrtConfigSetter // This means that the filter might have been installed before, // and the fix to the maximum payload size was already applied. // This needs to be checked now. - SrtFilterConfig fc; - if (!ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) + srt::SrtFilterConfig fc; + if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) { // Break silently. This should not happen LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); @@ -1128,9 +1128,9 @@ struct CSrtConfigSetter using namespace srt_logging; std::string arg((const char*)optval, optlen); // Parse the configuration string prematurely - SrtFilterConfig fc; - PacketFilter::Factory* fax = 0; - if (!ParseFilterConfig(arg, (fc), (&fax))) + srt::SrtFilterConfig fc; + srt::PacketFilter::Factory* fax = 0; + if (!srt::ParseFilterConfig(arg, (fc), (&fax))) { LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index bfd130148..dbe668743 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -24,6 +24,7 @@ written by #include "utilities.h" using namespace std; +using namespace srt; extern "C" { diff --git a/srtcore/udt.h b/srtcore/udt.h index 8d7d5eb87..b1af441b2 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -249,7 +249,7 @@ namespace logging { using namespace srt_logging; } -} +} // namespace srt // Planned deprecated removal: rel1.6.0 // There's also no portable way possible to enforce a deprecation diff --git a/srtcore/window.cpp b/srtcore/window.cpp index ea8386fe1..96562d7a4 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -151,7 +151,7 @@ void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeW r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec for (size_t i = 0; i < asize; ++ i) - r_bytesWindow[i] = CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] + r_bytesWindow[i] = srt::CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] } @@ -188,7 +188,7 @@ int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, cons // claculate speed, or return 0 if not enough valid value if (count > (asize >> 1)) { - bytes += (CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received + bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes))); return (int)ceil(1000000.0 / (sum / count)); } diff --git a/srtcore/window.h b/srtcore/window.h index 7dbfb73bf..d1065b3da 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -231,7 +231,7 @@ class CPktTimeWindow: CPktTimeWindowTools } /// Shortcut to test a packet for possible probe 1 or 2 - void probeArrival(const CPacket& pkt, bool unordered) + void probeArrival(const srt::CPacket& pkt, bool unordered) { const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; @@ -252,7 +252,7 @@ class CPktTimeWindow: CPktTimeWindowTools } /// Record the arrival time of the first probing packet. - void probe1Arrival(const CPacket& pkt, bool unordered) + void probe1Arrival(const srt::CPacket& pkt, bool unordered) { if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) { @@ -269,7 +269,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the second probing packet and the interval between packet pairs. - void probe2Arrival(const CPacket& pkt) + void probe2Arrival(const srt::CPacket& pkt) { // Reject probes that don't refer to the very next packet // towards the one that was lately notified by probe1Arrival. @@ -299,7 +299,7 @@ class CPktTimeWindow: CPktTimeWindowTools // record the probing packets interval // Adjust the time for what a complete packet would have take const int64_t timediff = srt::sync::count_microseconds(m_tsCurrArrTime - m_tsProbeTime); - const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE; + const int64_t timediff_times_pl_size = timediff * srt::CPacket::SRT_MAX_PAYLOAD_SIZE; // Let's take it simpler than it is coded here: // (stating that a packet has never zero size) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 795a6d52f..047ced7e2 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -2,6 +2,8 @@ #include "gtest/gtest.h" #include "buffer.h" +using namespace srt; + TEST(CRcvBuffer, Create) { diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 913473af7..8ea7ea26b 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -13,6 +13,7 @@ #include "api.h" using namespace std; +using namespace srt; class TestFECRebuilding: public testing::Test { @@ -91,16 +92,18 @@ class TestFECRebuilding: public testing::Test } }; -class TestMockCUDT -{ -public: - CUDT* core; - - bool checkApplyFilterConfig(const string& s) +namespace srt { + class TestMockCUDT { - return core->checkApplyFilterConfig(s); - } -}; + public: + CUDT* core; + + bool checkApplyFilterConfig(const string& s) + { + return core->checkApplyFilterConfig(s); + } + }; +} // The expected whole procedure of connection using FEC is // expected to: diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index df2ecca53..4a1cb0ec8 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -2,6 +2,8 @@ #include "common.h" #include "core.h" +using namespace srt; + const int32_t CSeqNo::m_iSeqNoTH; const int32_t CSeqNo::m_iMaxSeqNo; diff --git a/test/test_unitqueue.cpp b/test/test_unitqueue.cpp index f9010b9e7..324766f39 100644 --- a/test/test_unitqueue.cpp +++ b/test/test_unitqueue.cpp @@ -3,9 +3,8 @@ #include "gtest/gtest.h" #include "queue.h" - using namespace std; - +using namespace srt; /// Create CUnitQueue with queue size of 4 units. /// The size of 4 is chosen on purpose, because diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 1cb49cb64..501e5f8d7 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -2903,7 +2903,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; - if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) Error(SysError(), "Setting timeout for UDP"); } From 22cc92424acd650ee0238eb092df521e19434068 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 19 May 2021 12:15:08 +0200 Subject: [PATCH 082/683] [core] Applied clang-format on channel.h and cpp --- srtcore/channel.cpp | 574 ++++++++++++++++++++++---------------------- srtcore/channel.h | 128 +++++----- 2 files changed, 356 insertions(+), 346 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index c7b6e93f3..6e56e97f7 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -53,7 +53,7 @@ modified by #include "platform_sys.h" #include -#include // Logging +#include // Logging #include #include @@ -65,34 +65,30 @@ modified by #include "utilities.h" #ifdef _WIN32 - typedef int socklen_t; +typedef int socklen_t; #endif using namespace std; using namespace srt_logging; -namespace srt { +namespace srt +{ #ifdef _WIN32 - // use INVALID_SOCKET, as provided +// use INVALID_SOCKET, as provided #else - static const int INVALID_SOCKET = -1; +static const int INVALID_SOCKET = -1; #endif #if ENABLE_SOCK_CLOEXEC #ifndef _WIN32 -#if defined(_AIX) || \ - defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__linux__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) +#if defined(_AIX) || defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) // Set the CLOEXEC flag using ioctl() function -static int set_cloexec(int fd, int set) { +static int set_cloexec(int fd, int set) +{ int r; do @@ -106,7 +102,8 @@ static int set_cloexec(int fd, int set) { } #else // Set the CLOEXEC flag using fcntl() function -static int set_cloexec(int fd, int set) { +static int set_cloexec(int fd, int set) +{ int flags; int r; @@ -141,13 +138,11 @@ static int set_cloexec(int fd, int set) { } // namespace srt srt::CChannel::CChannel() - :m_iSocket(INVALID_SOCKET) + : m_iSocket(INVALID_SOCKET) { } -srt::CChannel::~CChannel() -{ -} +srt::CChannel::~CChannel() {} void srt::CChannel::createSocket(int family) { @@ -158,14 +153,14 @@ void srt::CChannel::createSocket(int family) m_iSocket = ::socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); if (m_iSocket == INVALID_SOCKET) { - m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); + m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; } #else - m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); + m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; #endif -#else // ENABLE_SOCK_CLOEXEC +#else // ENABLE_SOCK_CLOEXEC m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); #endif // ENABLE_SOCK_CLOEXEC @@ -174,10 +169,12 @@ void srt::CChannel::createSocket(int family) #if ENABLE_SOCK_CLOEXEC #ifdef _WIN32 - // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) + // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) #else - if (cloexec_flag) { - if (0 != set_cloexec(m_iSocket, 1)) { + if (cloexec_flag) + { + if (0 != set_cloexec(m_iSocket, 1)) + { throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); } } @@ -186,19 +183,19 @@ void srt::CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - const int res ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, - (const char*) &m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); + const int res ATR_UNUSED = + ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); #if ENABLE_LOGGING if (res == -1) { - int err = errno; + int err = errno; char msg[160]; - LOGC(kmlog.Error, log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " - << m_mcfg.iIpV6Only << ": " << SysStrError(err, msg, 159)); + LOGC(kmlog.Error, + log << "::setsockopt: failed to set IPPROTO_IPV6/IPV6_V6ONLY = " << m_mcfg.iIpV6Only << ": " + << SysStrError(err, msg, 159)); } #endif // ENABLE_LOGGING } - } void srt::CChannel::open(const sockaddr_any& addr) @@ -219,14 +216,14 @@ void srt::CChannel::open(int family) { createSocket(family); - //sendto or WSASendTo will also automatically bind the socket - addrinfo hints; + // sendto or WSASendTo will also automatically bind the socket + addrinfo hints; addrinfo* res; memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; const int eai = ::getaddrinfo(NULL, "0", &hints, &res); @@ -248,7 +245,7 @@ void srt::CChannel::open(int family) ::freeaddrinfo(res); throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); } - m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t) res->ai_addrlen); + m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t)res->ai_addrlen); ::freeaddrinfo(res); @@ -261,159 +258,169 @@ void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) { // The getsockname() call is done before calling it and the // result is placed into udpsocks_addr. - m_iSocket = udpsock; + m_iSocket = udpsock; m_BindAddr = udpsocks_addr; setUDPSockOpt(); } void srt::CChannel::setUDPSockOpt() { - #if defined(BSD) || TARGET_OS_MAC - // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value - int maxsize = 64000; - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &maxsize, sizeof maxsize); - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) - ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &maxsize, sizeof maxsize); - #else - // for other systems, if requested is greated than maximum, the maximum value will be automactally used - if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*) &m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) || - (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*) &m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - #endif - - if (m_mcfg.iIpTTL != -1) - { - if (m_BindAddr.family() == AF_INET) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - else - { - // If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS. - - // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*) &m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - } - } - - if (m_mcfg.iIpToS != -1) - { - if (m_BindAddr.family() == AF_INET) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - else - { - // If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS. +#if defined(BSD) || TARGET_OS_MAC + // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value + int maxsize = 64000; + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&maxsize, sizeof maxsize); + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&maxsize, sizeof maxsize); +#else + // for other systems, if requested is greated than maximum, the maximum value will be automactally used + if ((0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) || + (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); +#endif + + if (m_mcfg.iIpTTL != -1) + { + if (m_BindAddr.family() == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS. + + // For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || + !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != + ::setsockopt( + m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + // For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_mcfg.iIpTTL, sizeof m_mcfg.iIpTTL)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } + + if (m_mcfg.iIpToS != -1) + { + if (m_BindAddr.family() == AF_INET) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + else + { + // If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS. #ifdef IPV6_TCLASS - // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } + // For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || + !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt( + m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } #endif - // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 - if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) - { - if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*) &m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) - { - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } - } - } + // For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6 + if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr)) + { + if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_mcfg.iIpToS, sizeof m_mcfg.iIpToS)) + { + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } + } + } #ifdef SRT_ENABLE_BINDTODEVICE - if (!m_mcfg.sBindToDevice.empty()) - { - if (m_BindAddr.family() != AF_INET) - { - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE can only be set with AF_INET connections"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, - m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) - { + if (!m_mcfg.sBindToDevice.empty()) + { + if (m_BindAddr.family() != AF_INET) + { + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE can only be set with AF_INET connections"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (0 != ::setsockopt( + m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_mcfg.sBindToDevice.c_str(), m_mcfg.sBindToDevice.size())) + { #if ENABLE_LOGGING - char buf[255]; - const char* err = SysStrError(NET_ERROR, buf, 255); - LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); + char buf[255]; + const char* err = SysStrError(NET_ERROR, buf, 255); + LOGC(kmlog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); #endif // ENABLE_LOGGING - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } - } + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } #endif #ifdef UNIX - // Set non-blocking I/O - // UNIX does not support SO_RCVTIMEO - int opts = ::fcntl(m_iSocket, F_GETFL); - if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + // Set non-blocking I/O + // UNIX does not support SO_RCVTIMEO + int opts = ::fcntl(m_iSocket, F_GETFL); + if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #elif defined(_WIN32) - u_long nonBlocking = 1; - if (0 != ioctlsocket (m_iSocket, FIONBIO, &nonBlocking)) - throw CUDTException (MJ_SETUP, MN_NORES, NET_ERROR); + u_long nonBlocking = 1; + if (0 != ioctlsocket(m_iSocket, FIONBIO, &nonBlocking)) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #else - timeval tv; - tv.tv_sec = 0; -#if defined (BSD) || TARGET_OS_MAC - // Known BSD bug as the day I wrote this code. - // A small time out value will cause the socket to block forever. - tv.tv_usec = 10000; + timeval tv; + tv.tv_sec = 0; +#if defined(BSD) || TARGET_OS_MAC + // Known BSD bug as the day I wrote this code. + // A small time out value will cause the socket to block forever. + tv.tv_usec = 10000; #else - tv.tv_usec = 100; + tv.tv_usec = 100; #endif - // Set receiving time-out value - if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval))) - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + // Set receiving time-out value + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(timeval))) + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); #endif } void srt::CChannel::close() const { - #ifndef _WIN32 - ::close(m_iSocket); - #else - ::closesocket(m_iSocket); - #endif +#ifndef _WIN32 + ::close(m_iSocket); +#else + ::closesocket(m_iSocket); +#endif } int srt::CChannel::getSndBufSize() { - socklen_t size = (socklen_t) sizeof m_mcfg.iUDPSndBufSize; - ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*) &m_mcfg.iUDPSndBufSize, &size); - return m_mcfg.iUDPSndBufSize; + socklen_t size = (socklen_t)sizeof m_mcfg.iUDPSndBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_mcfg.iUDPSndBufSize, &size); + return m_mcfg.iUDPSndBufSize; } int srt::CChannel::getRcvBufSize() { - socklen_t size = (socklen_t) sizeof m_mcfg.iUDPRcvBufSize; - ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*) &m_mcfg.iUDPRcvBufSize, &size); - return m_mcfg.iUDPRcvBufSize; + socklen_t size = (socklen_t)sizeof m_mcfg.iUDPRcvBufSize; + ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_mcfg.iUDPRcvBufSize, &size); + return m_mcfg.iUDPRcvBufSize; } void srt::CChannel::setConfig(const CSrtMuxerConfig& config) @@ -423,50 +430,50 @@ void srt::CChannel::setConfig(const CSrtMuxerConfig& config) int srt::CChannel::getIpTTL() const { - if (m_iSocket == INVALID_SOCKET) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - socklen_t size = (socklen_t) sizeof m_mcfg.iIpTTL; - if (m_BindAddr.family() == AF_INET) - { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char*) &m_mcfg.iIpTTL, &size); - } - else if (m_BindAddr.family() == AF_INET6) - { - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char*) &m_mcfg.iIpTTL, &size); - } - else - { - // If family is unspecified, the socket probably doesn't exist. - LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - return m_mcfg.iIpTTL; + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + socklen_t size = (socklen_t)sizeof m_mcfg.iIpTTL; + if (m_BindAddr.family() == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char*)&m_mcfg.iIpTTL, &size); + } + else if (m_BindAddr.family() == AF_INET6) + { + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char*)&m_mcfg.iIpTTL, &size); + } + else + { + // If family is unspecified, the socket probably doesn't exist. + LOGC(kmlog.Error, log << "IPE: CChannel::getIpTTL called with unset family"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + return m_mcfg.iIpTTL; } int srt::CChannel::getIpToS() const { - if (m_iSocket == INVALID_SOCKET) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - socklen_t size = (socklen_t) sizeof m_mcfg.iIpToS; - if (m_BindAddr.family() == AF_INET) - { - ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char*) &m_mcfg.iIpToS, &size); - } - else if (m_BindAddr.family() == AF_INET6) - { + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + socklen_t size = (socklen_t)sizeof m_mcfg.iIpToS; + if (m_BindAddr.family() == AF_INET) + { + ::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char*)&m_mcfg.iIpToS, &size); + } + else if (m_BindAddr.family() == AF_INET6) + { #ifdef IPV6_TCLASS - ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char*) &m_mcfg.iIpToS, &size); + ::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char*)&m_mcfg.iIpToS, &size); #endif - } - else - { - // If family is unspecified, the socket probably doesn't exist. - LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - return m_mcfg.iIpToS; + } + else + { + // If family is unspecified, the socket probably doesn't exist. + LOGC(kmlog.Error, log << "IPE: CChannel::getIpToS called with unset family"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + return m_mcfg.iIpToS; } #ifdef SRT_ENABLE_BINDTODEVICE @@ -478,7 +485,7 @@ bool srt::CChannel::getBind(char* dst, size_t len) // Try to obtain it directly from the function. If not possible, // then return from internal data. socklen_t length = len; - int res = ::getsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, dst, &length); + int res = ::getsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, dst, &length); if (res == -1) return false; // Happens on Linux v < 3.8 @@ -492,8 +499,8 @@ int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) int value = 0; - int res = ::ioctl(m_iSocket, type, &value); - if ( res != -1 ) + int res = ::ioctl(m_iSocket, type, &value); + if (res != -1) return value; #endif return -1; @@ -502,10 +509,10 @@ int srt::CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const int srt::CChannel::sockoptQuery(int level SRT_ATR_UNUSED, int option SRT_ATR_UNUSED) const { #if defined(unix) || defined(__APPLE__) - int value = 0; - socklen_t len = sizeof (int); - int res = ::getsockopt(m_iSocket, level, option, &value, &len); - if ( res != -1 ) + int value = 0; + socklen_t len = sizeof(int); + int res = ::getsockopt(m_iSocket, level, option, &value, &len); + if (res != -1) return value; #endif return -1; @@ -517,26 +524,23 @@ void srt::CChannel::getSockAddr(sockaddr_any& w_addr) const // space to copy the socket name, it doesn't have to be correlated // with the address family. So the maximum space for any name, // regardless of the family, does the job. - socklen_t namelen = (socklen_t) w_addr.storage_size(); + socklen_t namelen = (socklen_t)w_addr.storage_size(); ::getsockname(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const { - socklen_t namelen = (socklen_t) w_addr.storage_size(); + socklen_t namelen = (socklen_t)w_addr.storage_size(); ::getpeername(m_iSocket, (w_addr.get()), (&namelen)); w_addr.len = namelen; } - int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { - HLOGC(kslog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << addr.str() - << " target=@" << packet.m_iID - << " size=" << packet.getLength() - << " pkt.ts=" << packet.m_iTimeStamp - << " " << packet.Info()); + HLOGC(kslog.Debug, + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID + << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS @@ -546,18 +550,18 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const #undef FAKELOSS_STRING #undef FAKELOSS_WRAP - static int dcounter = 0; + static int dcounter = 0; static int flwcounter = 0; struct FakelossConfig { - pair config; + pair config; FakelossConfig(const char* f) { vector out; Split(f, '+', back_inserter(out)); - config.first = atoi(out[0].c_str()); + config.first = atoi(out[0].c_str()); config.second = out.size() > 1 ? atoi(out[1].c_str()) : 8; } }; @@ -571,7 +575,9 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { // This is a counter of how many packets in a row shall be lost --flwcounter; - HLOGC(kslog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter << " more to drop)"); + HLOGC(kslog.Debug, + log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter + << " more to drop)"); return packet.getLength(); // fake successful sendinf } @@ -583,7 +589,9 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > rnd) { dcounter = 1; - HLOGC(kslog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " << fakeloss.config.first << " more)"); + HLOGC(kslog.Debug, + log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " + << fakeloss.config.first << " more)"); flwcounter = fakeloss.config.first; return packet.getLength(); // fake successful sendinf } @@ -592,51 +600,51 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const #endif - // convert control information into network order - packet.toNL(); - - #ifndef _WIN32 - msghdr mh; - mh.msg_name = (sockaddr*)&addr; - mh.msg_namelen = addr.size(); - mh.msg_iov = (iovec*)packet.m_PacketVector; - mh.msg_iovlen = 2; - mh.msg_control = NULL; - mh.msg_controllen = 0; - mh.msg_flags = 0; - - const int res = ::sendmsg(m_iSocket, &mh, 0); - #else - DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength()); - int addrsize = addr.size(); - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL); - res = (0 == res) ? size : -1; - #endif - - packet.toHL(); - - return res; + // convert control information into network order + packet.toNL(); + +#ifndef _WIN32 + msghdr mh; + mh.msg_name = (sockaddr*)&addr; + mh.msg_namelen = addr.size(); + mh.msg_iov = (iovec*)packet.m_PacketVector; + mh.msg_iovlen = 2; + mh.msg_control = NULL; + mh.msg_controllen = 0; + mh.msg_flags = 0; + + const int res = ::sendmsg(m_iSocket, &mh, 0); +#else + DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); + int addrsize = addr.size(); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, NULL, NULL); + res = (0 == res) ? size : -1; +#endif + + packet.toHL(); + + return res; } EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const { - EReadStatus status = RST_OK; - int msg_flags = 0; - int recv_size = -1; + EReadStatus status = RST_OK; + int msg_flags = 0; + int recv_size = -1; #if defined(UNIX) || defined(_WIN32) - fd_set set; + fd_set set; timeval tv; FD_ZERO(&set); FD_SET(m_iSocket, &set); - tv.tv_sec = 0; - tv.tv_usec = 10000; - const int select_ret = ::select((int) m_iSocket + 1, &set, NULL, &set, &tv); + tv.tv_sec = 0; + tv.tv_usec = 10000; + const int select_ret = ::select((int)m_iSocket + 1, &set, NULL, &set, &tv); #else - const int select_ret = 1; // the socket is expected to be in the blocking mode itself + const int select_ret = 1; // the socket is expected to be in the blocking mode itself #endif - if (select_ret == 0) // timeout + if (select_ret == 0) // timeout { w_packet.setLength(-1); return RST_AGAIN; @@ -646,13 +654,13 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con if (select_ret > 0) { msghdr mh; - mh.msg_name = (w_addr.get()); - mh.msg_namelen = w_addr.size(); - mh.msg_iov = (w_packet.m_PacketVector); - mh.msg_iovlen = 2; - mh.msg_control = NULL; + mh.msg_name = (w_addr.get()); + mh.msg_namelen = w_addr.size(); + mh.msg_iov = (w_packet.m_PacketVector); + mh.msg_iovlen = 2; + mh.msg_control = NULL; mh.msg_controllen = 0; - mh.msg_flags = 0; + mh.msg_flags = 0; recv_size = ::recvmsg(m_iSocket, (&mh), 0); msg_flags = mh.msg_flags; @@ -683,7 +691,8 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con if (select_ret == -1 || recv_size == -1) { const int err = NET_ERROR; - if (err == EAGAIN || err == EINTR || err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call. + if (err == EAGAIN || err == EINTR || + err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call. { status = RST_AGAIN; } @@ -713,16 +722,23 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // value one Windows than 0, unless this procedure below is rewritten // to use WSARecvMsg(). - int recv_ret = SOCKET_ERROR; - DWORD flag = 0; + int recv_ret = SOCKET_ERROR; + DWORD flag = 0; - if (select_ret > 0) // the total number of socket handles that are ready + if (select_ret > 0) // the total number of socket handles that are ready { - DWORD size = (DWORD) (CPacket::HDR_SIZE + w_packet.getLength()); - int addrsize = w_addr.size(); - - recv_ret = ::WSARecvFrom(m_iSocket, ((LPWSABUF)w_packet.m_PacketVector), 2, - (&size), (&flag), (w_addr.get()), (&addrsize), NULL, NULL); + DWORD size = (DWORD)(CPacket::HDR_SIZE + w_packet.getLength()); + int addrsize = w_addr.size(); + + recv_ret = ::WSARecvFrom(m_iSocket, + ((LPWSABUF)w_packet.m_PacketVector), + 2, + (&size), + (&flag), + (w_addr.get()), + (&addrsize), + NULL, + NULL); if (recv_ret == 0) recv_size = size; } @@ -737,16 +753,9 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // WSAETIMEDOUT, which isn't mentioned in the documentation of WSARecvFrom at all. // // These below errors are treated as "fatal", all others are treated as "again". - static const int fatals [] = - { - WSAEFAULT, - WSAEINVAL, - WSAENETDOWN, - WSANOTINITIALISED, - WSA_OPERATION_ABORTED - }; + static const int fatals[] = {WSAEFAULT, WSAEINVAL, WSAENETDOWN, WSANOTINITIALISED, WSA_OPERATION_ABORTED}; static const int* fatals_end = fatals + Size(fatals); - const int err = NET_ERROR; + const int err = NET_ERROR; if (std::find(fatals, fatals_end, err) != fatals_end) { HLOGC(krlog.Debug, log << CONID() << "(sys)WSARecvFrom: " << SysStrError(err) << " [" << err << "]"); @@ -765,12 +774,12 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con msg_flags = 1; #endif - // Sanity check for a case when it didn't fill in even the header if (size_t(recv_size) < CPacket::HDR_SIZE) { status = RST_AGAIN; - HLOGC(krlog.Debug, log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes"); + HLOGC(krlog.Debug, + log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes"); goto Return_error; } @@ -791,10 +800,11 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // When this happens, then you have at best a fragment of the buffer and it's // useless anyway. This is solved by dropping the packet and fake that no // packet was received, so the packet will be then retransmitted. - if ( msg_flags != 0 ) + if (msg_flags != 0) { - HLOGC(krlog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size - << " msg_flags=0x" << hex << msg_flags << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")"); + HLOGC(krlog.Debug, + log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" << hex << msg_flags + << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")"); status = RST_AGAIN; goto Return_error; } @@ -803,21 +813,21 @@ EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) con // convert back into local host order // XXX use NtoHLA(). - //for (int i = 0; i < 4; ++ i) + // for (int i = 0; i < 4; ++ i) // w_packet.m_nHeader[i] = ntohl(w_packet.m_nHeader[i]); { uint32_t* p = w_packet.m_nHeader; - for (size_t i = 0; i < SRT_PH_E_SIZE; ++ i) + for (size_t i = 0; i < SRT_PH_E_SIZE; ++i) { *p = ntohl(*p); - ++ p; + ++p; } } if (w_packet.isControl()) { - for (size_t j = 0, n = w_packet.getLength() / sizeof (uint32_t); j < n; ++ j) - *((uint32_t *)w_packet.m_pcData + j) = ntohl(*((uint32_t *)w_packet.m_pcData + j)); + for (size_t j = 0, n = w_packet.getLength() / sizeof(uint32_t); j < n; ++j) + *((uint32_t*)w_packet.m_pcData + j) = ntohl(*((uint32_t*)w_packet.m_pcData + j)); } return RST_OK; diff --git a/srtcore/channel.h b/srtcore/channel.h index ad6b3f785..0255102fe 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -59,107 +59,107 @@ modified by #include "socketconfig.h" #include "netinet_any.h" -namespace srt { +namespace srt +{ class CChannel { - void createSocket(int family); -public: + void createSocket(int family); - // XXX There's currently no way to access the socket ID set for - // whatever the channel is currently working for. Required to find - // some way to do this, possibly by having a "reverse pointer". - // Currently just "unimplemented". - std::string CONID() const { return ""; } +public: + // XXX There's currently no way to access the socket ID set for + // whatever the channel is currently working for. Required to find + // some way to do this, possibly by having a "reverse pointer". + // Currently just "unimplemented". + std::string CONID() const { return ""; } - CChannel(); - ~CChannel(); + CChannel(); + ~CChannel(); - /// Open a UDP channel. - /// @param [in] addr The local address that UDP will use. + /// Open a UDP channel. + /// @param [in] addr The local address that UDP will use. - void open(const sockaddr_any& addr); + void open(const sockaddr_any& addr); - void open(int family); + void open(int family); - /// Open a UDP channel based on an existing UDP socket. - /// @param [in] udpsock UDP socket descriptor. + /// Open a UDP channel based on an existing UDP socket. + /// @param [in] udpsock UDP socket descriptor. - void attach(UDPSOCKET udpsock, const sockaddr_any& adr); + void attach(UDPSOCKET udpsock, const sockaddr_any& adr); - /// Disconnect and close the UDP entity. + /// Disconnect and close the UDP entity. - void close() const; + void close() const; - /// Get the UDP sending buffer size. - /// @return Current UDP sending buffer size. + /// Get the UDP sending buffer size. + /// @return Current UDP sending buffer size. - int getSndBufSize(); + int getSndBufSize(); - /// Get the UDP receiving buffer size. - /// @return Current UDP receiving buffer size. + /// Get the UDP receiving buffer size. + /// @return Current UDP receiving buffer size. - int getRcvBufSize(); + int getRcvBufSize(); - /// Query the socket address that the channel is using. - /// @param [out] addr pointer to store the returned socket address. + /// Query the socket address that the channel is using. + /// @param [out] addr pointer to store the returned socket address. - void getSockAddr(sockaddr_any& addr) const; + void getSockAddr(sockaddr_any& addr) const; - /// Query the peer side socket address that the channel is connect to. - /// @param [out] addr pointer to store the returned socket address. + /// Query the peer side socket address that the channel is connect to. + /// @param [out] addr pointer to store the returned socket address. - void getPeerAddr(sockaddr_any& addr) const; + void getPeerAddr(sockaddr_any& addr) const; - /// Send a packet to the given address. - /// @param [in] addr pointer to the destination address. - /// @param [in] packet reference to a CPacket entity. - /// @return Actual size of data sent. + /// Send a packet to the given address. + /// @param [in] addr pointer to the destination address. + /// @param [in] packet reference to a CPacket entity. + /// @return Actual size of data sent. - int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; + int sendto(const sockaddr_any& addr, srt::CPacket& packet) const; - /// Receive a packet from the channel and record the source address. - /// @param [in] addr pointer to the source address. - /// @param [in] packet reference to a CPacket entity. - /// @return Actual size of data received. + /// Receive a packet from the channel and record the source address. + /// @param [in] addr pointer to the source address. + /// @param [in] packet reference to a CPacket entity. + /// @return Actual size of data received. - EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; + EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const; - void setConfig(const CSrtMuxerConfig& config); + void setConfig(const CSrtMuxerConfig& config); - /// Get the IP TTL. - /// @param [in] ttl IP Time To Live. - /// @return TTL. + /// Get the IP TTL. + /// @param [in] ttl IP Time To Live. + /// @return TTL. - int getIpTTL() const; + int getIpTTL() const; - /// Get the IP Type of Service. - /// @return ToS. + /// Get the IP Type of Service. + /// @return ToS. - int getIpToS() const; + int getIpToS() const; #ifdef SRT_ENABLE_BINDTODEVICE - bool getBind(char* dst, size_t len); + bool getBind(char* dst, size_t len); #endif - int ioctlQuery(int type) const; - int sockoptQuery(int level, int option) const; + int ioctlQuery(int type) const; + int sockoptQuery(int level, int option) const; - const sockaddr* bindAddress() { return m_BindAddr.get(); } - const sockaddr_any& bindAddressAny() { return m_BindAddr; } + const sockaddr* bindAddress() { return m_BindAddr.get(); } + const sockaddr_any& bindAddressAny() { return m_BindAddr; } private: - void setUDPSockOpt(); + void setUDPSockOpt(); private: + UDPSOCKET m_iSocket; // socket descriptor - UDPSOCKET m_iSocket; // socket descriptor - - // Mutable because when querying original settings - // this comprises the cache for extracted values, - // although the object itself isn't considered modified. - mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. - sockaddr_any m_BindAddr; + // Mutable because when querying original settings + // this comprises the cache for extracted values, + // although the object itself isn't considered modified. + mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective. + sockaddr_any m_BindAddr; }; } // namespace srt From 44eb6cedebb14aeecb66066d3d590aeb127b2e6e Mon Sep 17 00:00:00 2001 From: LELEGARD Thierry Date: Thu, 27 May 2021 11:35:01 +0200 Subject: [PATCH 083/683] [build] Improved Windows installer - More comprehensive README. - Added a sample installation script for users to automate the download and installation of libsrt. Useful to automate user's CI/CD pipeline. - Renamed typo in script name ("intaller"). - Updated build script to also produce the .zip archive as published with version 1.4.3. But note that binaries don't compress well (only 2% space) and publishing a .zip instead of the .exe does not provide much improvement anyway. --- scripts/win-installer/README.md | 122 +++++++++++++--- ...n-intaller.ps1 => build-win-installer.ps1} | 15 ++ scripts/win-installer/install-libsrt.ps1 | 131 ++++++++++++++++++ 3 files changed, 252 insertions(+), 16 deletions(-) rename scripts/win-installer/{build-win-intaller.ps1 => build-win-installer.ps1} (93%) create mode 100644 scripts/win-installer/install-libsrt.ps1 diff --git a/scripts/win-installer/README.md b/scripts/win-installer/README.md index 205e03893..ff54df258 100644 --- a/scripts/win-installer/README.md +++ b/scripts/win-installer/README.md @@ -1,33 +1,123 @@ -## SRT Static Libraries Installer for Windows +# SRT Static Libraries Installer for Windows This directory contains scripts to build a binary installer for libsrt on Windows systems for Visual Studio applications using SRT. -### Building Windows applications with libsrt +## SRT developer: Building the libsrt installer + +### Prerequisites + +These first two steps need to be executed once only. + +- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. + This can be done automatically by running the PowerShell script `install-openssl.ps1`. + +- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. + This can be done automatically by running the PowerShell script `install-nsis.ps1`. + +### Building the libsrt installer + +To build the libsrt installer, simply run the PowerShell script `build-win-installer.ps1`. +Running it without parameters, for instance launching it from the Windows Explorer, is +sufficient to build the installer. + +Optional parameters: + +- `-Version name` : + Use the specified string as version number for libsrt. By default, if the + current commit has a tag, use that tag (initial "v" removed, for instance + `1.4.3`). Otherwise, the defaut version is a detailed version number (most + recent version, number of commits since then, short commit SHA, for instance + `1.4.3-32-g22cc924`). Use that option if necessary to specify some other + non-standard form of version string. + +- `-NoPause` : + Do not wait for the user to press `` at end of execution. By default, + execute a `pause` instruction at the end of execution, which is useful + when the script was launched from Windows Explorer. Use that option when the + script is invoked from another PowerShell script. + +The installer is then available in the directory `installers`. + +The name of the installer is `libsrt-VERS.exe` where `VERS` is the SRT version number +(see the `-Version` option). + +The installer shall then be published as a release asset in the `srt` repository +on GitHub, either as `libsrt-VERS.exe` or `libsrt-VERS-win-installer.zip`. +In the latter case, the archive shall contain `libsrt-VERS.exe`. + +## SRT user: Using the libsrt installer -After installing the libsrt binary, an environment variable named `LIBSRT` is -defined to the installation root (typically `C:\Program Files (x86)\libsrt`). +### Installing the SRT libraries -In this directory, there is a Visual Studio property file named `libsrt.props`. -Simply reference this property file in your Visual Studio project to use libsrt. +To install the SRT libraries, simply run the `libsrt-VERS.exe` installer which is +available in the [SRT release area](https://github.com/Haivision/srt/releases). + +After installing the libsrt binaries, an environment variable named `LIBSRT` is +defined with the installation root (typically `C:\Program Files (x86)\libsrt`). + +If there is a need for automation, in a CI/CD pipeline for instance, the download +of the latest `libsrt-VERS.exe` and its installation can be automated using the +sample PowerShell script `install-libsrt.ps1` which is available in this directory. +This script may be freely copied in the user's build environment. + +When run without parameters (for instance from the Windows explorer), this +script downloads and installs the latest version of libsrt. + +Optional parameters: + +- `-Destination path` : + Specify a local directory where the libsrt package will be downloaded. + By default, use the `tmp` subdirectory from this script's directory. + +- `-ForceDownload` : + Force a download even if the package is already downloaded in the + destination path. Note that the latest version is always checked. + If a older package is already present but a newer one is available + online, the newer one is always downloaded, even without this option. + +- `-GitHubActions` : + When used in a GitHub Actions workflow, make sure that the `LIBSRT` + environment variable is propagated to subsequent jobs. In your GitHub + workflow, in the initial setup phase, use + `script-dir\install-libsrt.ps1 -GitHubActions -NoPause`. + +- `-NoInstall` : + Do not install the package, only download it. By default, libsrt is installed. + +- `-NoPause` : + Do not wait for the user to press `` at end of execution. By default, + execute a `pause` instruction at the end of execution, which is useful + when the script was launched from Windows Explorer. Use that option when the + script is invoked from another PowerShell script. + +### Building Windows applications with libsrt + +In the SRT installation root directory (specified in environment variable `LIBSRT`), +there is a Visual Studio property file named `libsrt.props`. Simply reference this +property file in your Visual Studio project to use libsrt. You can also do that manually by editing the application project file (the XML file named with a `.vcxproj` extension). Add the following line just before the end of the file: ~~~ - + ~~~ -### Building the installer +With this setup, just compile your application normally, either using the +Visual Studio IDE or the MSBuild command line tool. -The first two steps need to be executed once only. Only the last step needs -to be repeated each time a new version of libsrt is available. +## Files reference -- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits. - This can be done automatically by running the PowerShell script `install-openssl.ps1`. -- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system. - This can be done automatically by running the PowerShell script `install-nsis.ps1`. -- Build the libsrt installer by running the PowerShell script `build-win-installer.ps1`. +This directory contains the following files: -The installer is then available in the directory `installers`. +| File name | Usage +| ----------------------- | ----- +| build-win-installer.ps1 | PowerShell script to build the libsrt installer. +| install-libsrt.ps1 | Sample PowerShell script to automatically install libsrt (for user's projects). +| install-openssl.ps1 | PowerShell script to install OpenSSL (prerequisite to build the installer). +| install-nsis.ps1 | PowerShell script to install NSIS (prerequisite to build the installer). +| libsrt.nsi | NSIS installation script (used to build the installer). +| libsrt.props | Visual Studio property files to use libsrt (embedded in the installer). +| README.md | This text file. diff --git a/scripts/win-installer/build-win-intaller.ps1 b/scripts/win-installer/build-win-installer.ps1 similarity index 93% rename from scripts/win-installer/build-win-intaller.ps1 rename to scripts/win-installer/build-win-installer.ps1 index 261c457fa..4265edf8e 100644 --- a/scripts/win-installer/build-win-intaller.ps1 +++ b/scripts/win-installer/build-win-installer.ps1 @@ -200,6 +200,9 @@ if ($Missing -gt 0) { # Build the binary installer. #----------------------------------------------------------------------------- +$InstallExe = "$OutDir\libsrt-$Version.exe" +$InstallZip = "$OutDir\libsrt-$Version-win-installer.zip" + Write-Output "Building installer ..." & $NSIS /V2 ` /DVersion="$Version" ` @@ -209,4 +212,16 @@ Write-Output "Building installer ..." /DRepoDir="$RepoDir" ` "$ScriptDir\libsrt.nsi" +if (-not (Test-Path $InstallExe)) { + Exit-Script "**** Missing $InstallExe" +} + +Write-Output "Building installer archive ..." +Remove-Item -Force -ErrorAction SilentlyContinue $InstallZip +Compress-Archive -Path $InstallExe -DestinationPath $InstallZip -CompressionLevel Optimal + +if (-not (Test-Path $InstallZip)) { + Exit-Script "**** Missing $InstallZip" +} + Exit-Script diff --git a/scripts/win-installer/install-libsrt.ps1 b/scripts/win-installer/install-libsrt.ps1 new file mode 100644 index 000000000..ae1b8133e --- /dev/null +++ b/scripts/win-installer/install-libsrt.ps1 @@ -0,0 +1,131 @@ +# SRT library download and install for Windows. +# Copyright (c) 2021, Thierry Lelegard +# All rights reserved. + +<# + .SYNOPSIS + + Download and install the libsrt library for Windows. This script is + provided to automate the build of Windows applications using libsrt. + + .PARAMETER Destination + + Specify a local directory where the libsrt package will be downloaded. + By default, use "tmp" subdirectory from this script. + + .PARAMETER ForceDownload + + Force a download even if the package is already downloaded. + + .PARAMETER GitHubActions + + When used in a GitHub Actions workflow, make sure that the LIBSRT + environment variable is propagated to subsequent jobs. + + .PARAMETER NoInstall + + Do not install the package. By default, libsrt is installed. + + .PARAMETER NoPause + + Do not wait for the user to press at end of execution. By default, + execute a "pause" instruction at the end of execution, which is useful + when the script was run from Windows Explorer. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +param( + [string]$Destination = "", + [switch]$ForceDownload = $false, + [switch]$GitHubActions = $false, + [switch]$NoInstall = $false, + [switch]$NoPause = $false +) + +Write-Output "libsrt download and installation procedure" + +# Default directory for downloaded products. +if (-not $Destination) { + $Destination = "$PSScriptRoot\tmp" +} + +# A function to exit this script. +function Exit-Script([string]$Message = "") +{ + $Code = 0 + if ($Message -ne "") { + Write-Output "ERROR: $Message" + $Code = 1 + } + if (-not $NoPause) { + pause + } + exit $Code +} + +# Without this, Invoke-WebRequest is awfully slow. +$ProgressPreference = 'SilentlyContinue' + +# Get the URL of the latest libsrt installer. +$URL = (Invoke-RestMethod "https://api.github.com/repos/Haivision/srt/releases?per_page=20" | + ForEach-Object { $_.assets } | + ForEach-Object { $_.browser_download_url } | + Select-String @("/libsrt-.*\.exe$", "/libsrt-.*-win-installer\.zip$") | + Select-Object -First 1) + +if (-not $URL) { + Exit-Script "Could not find a libsrt installer on GitHub" +} +if (-not ($URL -match "\.zip$") -and -not ($URL -match "\.exe$")) { + Exit-Script "Unexpected URL, not .exe, not .zip: $URL" +} + +# Installer name and path. +$InstName = (Split-Path -Leaf $URL) +$InstPath = "$Destination\$InstName" + +# Create the directory for downloaded products when necessary. +[void](New-Item -Path $Destination -ItemType Directory -Force) + +# Download installer +if (-not $ForceDownload -and (Test-Path $InstPath)) { + Write-Output "$InstName already downloaded, use -ForceDownload to download again" +} +else { + Write-Output "Downloading $URL ..." + Invoke-WebRequest $URL.ToString() -UseBasicParsing -UserAgent Download -OutFile $InstPath + if (-not (Test-Path $InstPath)) { + Exit-Script "$URL download failed" + } +} + +# If installer is an archive, expect an exe with same name inside. +if ($InstName -match "\.zip$") { + + # Expected installer name in archive. + $ZipName = $InstName + $ZipPath = $InstPath + $InstName = $ZipName -replace '-win-installer.zip','.exe' + $InstPath = "$Destination\$InstName" + + # Extract the installer. + Remove-Item -Force $InstPath -ErrorAction SilentlyContinue + Write-Output "Expanding $ZipName ..." + Expand-Archive $ZipPath -DestinationPath $Destination + if (-not (Test-Path $InstPath)) { + Exit-Script "$InstName not found in $ZipName" + } +} + +# Install libsrt +if (-not $NoInstall) { + Write-Output "Installing $InstName" + Start-Process -FilePath $InstPath -ArgumentList @("/S") -Wait +} + +# Propagate LIBSRT in next jobs for GitHub Actions. +if ($GitHubActions -and (-not -not $env:GITHUB_ENV) -and (Test-Path $env:GITHUB_ENV)) { + $libsrt = [System.Environment]::GetEnvironmentVariable("LIBSRT","Machine") + Write-Output "LIBSRT=$libsrt" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append +} + +Exit-Script From e2a00aa03a36e153ab8cfe2c0790f7bf43747d8d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 28 May 2021 11:04:53 +0200 Subject: [PATCH 084/683] [core] Detect reusable bindings and binding conflicts correctly (#1588). Introduced new SRT socket error SRT_EBINDCONFLICT. --- .travis.yml | 5 +- docs/API/API-functions.md | 86 ++++-- scripts/generate-error-types.tcl | 18 +- srtcore/api.cpp | 189 +++++++++---- srtcore/api.h | 5 + srtcore/srt.h | 2 + srtcore/strerror_defs.cpp | 12 +- test/filelist.maf | 1 + test/test_reuseaddr.cpp | 440 +++++++++++++++++++++++++++++++ test/test_sync.cpp | 4 +- 10 files changed, 672 insertions(+), 90 deletions(-) create mode 100644 test/test_reuseaddr.cpp diff --git a/.travis.yml b/.travis.yml index 4ffe49add..51631c086 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ matrix: - make - cd .. env: BUILD_TYPE=Release - + # Power jobs - os: linux arch: ppc64le @@ -69,6 +69,7 @@ matrix: - BUILD_TYPE=Release - BUILD_OPTS='-DENABLE_MONOTONIC_CLOCK=ON' script: + - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*:ReuseAddr.ProtocolVersion" ; # Tests to skip due to lack of IPv6 support - if [ "$TRAVIS_COMPILER" == "x86_64-w64-mingw32-g++" ]; then export CC="x86_64-w64-mingw32-gcc"; export CXX="x86_64-w64-mingw32-g++"; @@ -91,7 +92,7 @@ script: make -j$(nproc); fi - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then - ./test-srt --gtest_filter="-TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*"; + ./test-srt --gtest_filter="-$TESTS_IPv6"; fi - if (( "$RUN_CODECOV" )); then source ./scripts/collect-gcov.sh; diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 44f8ac19a..5a1fffd70 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -384,13 +384,14 @@ connecting, use [`srt_connect_bind`](#srt_connect_bind) for that purpose. | `SRT_ERROR` | (-1) on error, otherwise 0 | | | | -| Errors | | -|:----------------------------------- |:-------------------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | -| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | -| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | -| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | -| | | +| Errors | | +|:---------------------------------------- |:-------------------------------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | +| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | +| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | +| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | +| [`SRT_EBINDCONFLICT`](#srt_ebindconflict)| Binding specification conflicts with existing one | +| | | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -841,18 +842,19 @@ first on the automatically created socket for the connection. | `SRT_ERROR` | (-1) in case of error | | 0 | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | -| | | +| | | -| Errors | | -|:------------------------------------- |:-------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | -| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | -| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | -| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | -| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Internal error ([`srt_connect`](#srt_connect) should not report it after [`srt_bind`](#srt_bind) was called) | -| [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | -| [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | -| | | +| Errors | | +|:---------------------------------------- |:-------------------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | +| [`SRT_EINVOP`](#srt_einvop) | Socket already bound | +| [`SRT_ECONNSETUP`](#srt_econnsetup) | Internal creation of a UDP socket failed | +| [`SRT_ESOCKFAIL`](#srt_esockfail) | Internal configuration of a UDP socket (`bind`, `setsockopt`) failed | +| [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Internal error ([`srt_connect`](#srt_connect) should not report it after [`srt_bind`](#srt_bind) was called) | +| [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | +| [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | +| [`SRT_EBINDCONFLICT`](#srt_ebindconflict)| Binding specification conflicts with existing one | +| | | **IMPORTANT**: It's not allowed to bind and connect the same socket to two @@ -3225,6 +3227,54 @@ by setting the `SRT_EPOLL_ENABLE_EMPTY` flag, which may be useful when you use multiple threads and start waiting without subscribed sockets, so that you can subscribe them later from another thread. + +#### `SRT_EBINDCONFLICT` + +The binding you are attempting to set up a socket with cannot be completed because +it conflicts with another existing binding. This is because an intersecting binding +was found that cannot be reused according to the specification in `srt_bind` call. + +A binding is considered intersecting if the existing binding has the same port +and covers at least partially the range as that of the attempted binding. These +ranges can be split in three groups: + +1. An explicitly specified IP address (both IPv4 and IPv6) covers this address only. +2. An IPv4 wildcard 0.0.0.0 covers all IPv4 addresses (but not IPv6). +3. An IPv6 wildcard :: covers: + * if `SRTO_IPV6ONLY` is true - all IPv6 addresses (but not IPv4) + * if `SRTO_IPV6ONLY` is false - all IP addresses. + +Example 1: + +* Socket 1: bind to IPv4 0.0.0.0 +* Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = true +* Result: NOT intersecting + +Example 2: + +* Socket 1: bind to IPv4 1.2.3.4 +* Socket 2: bind to IPv4 0.0.0.0 +* Result: intersecting (and conflicting) + +Example 3: + +* Socket 1: bind to IPv4 1.2.3.4 +* Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = false +* Result: intersecting (and conflicting) + +If any common range coverage is found between the attempted binding specification +(in `srt_bind` call) and the found existing binding with the same port number, +then all of the following conditions must be satisfied between them: + +1. The `SRTO_REUSEADDR` must be true (default) in both. + +2. The IP address specification (in case of IPv6, also including the value of +`SRTO_IPV6ONLY` flag) must be exactly identical. + +3. The UDP-specific settings must be identical. + +If any of these conditions isn't satisfied, the `srt_bind` function results +in conflict and report this error. #### SRT_EASYNCFAIL diff --git a/scripts/generate-error-types.tcl b/scripts/generate-error-types.tcl index 15c8c0fa2..b51d60eb1 100755 --- a/scripts/generate-error-types.tcl +++ b/scripts/generate-error-types.tcl @@ -61,6 +61,7 @@ set code_minor { XSIZE 12 EIDINVAL 13 EEMPTY 14 + BUSYPORT 15 WRAVAIL 1 RDAVAIL 2 @@ -121,7 +122,7 @@ set errortypes { XSIZE "Message is too large to send (it must be less than the SRT send buffer size)" EIDINVAL "Invalid epoll ID" EEMPTY "All sockets removed from epoll, waiting would deadlock" - + BUSYPORT "Another socket is bound to that port and is not reusable for requested settings" } AGAIN "Non-blocking call failure" { @@ -142,11 +143,10 @@ set main_array_item { const char** strerror_array_major [] = { $minor_array_list }; - } set major_size_item { -size_t strerror_array_sizes [] = { +const size_t strerror_array_sizes [] = { $minor_array_sizes }; } @@ -155,11 +155,9 @@ set minor_array_item { const char* strerror_msgs_$majorlc [] = { $minor_message_items }; - } set strerror_function { - const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; @@ -228,16 +226,17 @@ proc Generate:imp {} { puts [subst -nobackslashes -nocommands $::minor_array_item] append minor_array_list " strerror_msgs_$majorlc, // MJ_$mt = $majitem\n" - append minor_array_sizes " [expr {$minitem}],\n" + #append minor_array_sizes " [expr {$minitem}],\n" + append minor_array_sizes " SRT_ARRAY_SIZE(strerror_msgs_$majorlc) - 1,\n" incr majitem } - append minor_array_list " NULL\n" - append minor_array_sizes " 0\n" + append minor_array_list " NULL" + append minor_array_sizes " 0" puts [subst -nobackslashes -nocommands $::main_array_item] + puts {#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0])} puts [subst -nobackslashes -nocommands $::major_size_item] - puts "" puts $::strerror_function puts "\} // namespace srt" @@ -250,4 +249,3 @@ if {[lindex $argv 0] != ""} { } Generate:$defmode - diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 44a068764..ad243d50e 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2829,51 +2829,150 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) } } -void srt::CUDTUnited::updateMux( - CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) +void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { - ScopedLock cg(m_GlobControlLock); + w_m.m_mcfg = s->m_pUDT->m_config; + w_m.m_iIPversion = af; + w_m.m_iRefCount = 1; + w_m.m_iID = s->m_SocketID; +} - // Don't try to reuse given address, if udpsock was given. - // In such a case rely exclusively on that very socket and - // use it the way as it is configured, of course, create also - // always a new multiplexer for that very socket. - if (!udpsock && s->m_pUDT->m_config.bReuseAddr) - { - const int port = addr.hport(); +uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) +{ + w_s->m_pUDT->m_pSndQueue = fw_sm.m_pSndQueue; + w_s->m_pUDT->m_pRcvQueue = fw_sm.m_pRcvQueue; + w_s->m_iMuxID = fw_sm.m_iID; + sockaddr_any sa; + fw_sm.m_pChannel->getSockAddr((sa)); + w_s->m_SelfAddr = sa; // Will be also completed later, but here it's needed for later checks + return sa.hport(); +} - // find a reusable address - for (map::iterator i = m_mMultiplexer.begin(); - i != m_mMultiplexer.end(); ++ i) - { - // Use the "family" value blindly from the address; we - // need to find an existing multiplexer that binds to the - // given port in the same family as requested address. - if ((i->second.m_iIPversion == addr.family()) - && i->second.m_mcfg == s->m_pUDT->m_config) - { - if (i->second.m_iPort == port) +bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) +{ + return m.m_mcfg.bReuseAddr && m.m_mcfg == s->m_pUDT->m_config; +} + +void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) +{ + ScopedLock cg(m_GlobControlLock); + + // If udpsock is provided, then this socket will be simply + // taken for binding as a good deal. It would be nice to make + // a sanity check to see if this UDP socket isn't already installed + // in some multiplexer, but we state this UDP socket isn't accessible + // anyway so this wouldn't be possible. + if (!udpsock) + { + // If not, we need to see if there exist already a multiplexer bound + // to the same endpoint. + const int port = addr.hport(); + + bool reuse_attempt = false; + for (map::iterator i = m_mMultiplexer.begin(); + i != m_mMultiplexer.end(); ++ i) + { + CMultiplexer& m = i->second; + + // First, we need to find a multiplexer with the same port. + if (m.m_iPort != port) { - // HLOGF(smlog.Debug, "reusing multiplexer for port - // %hd\n", port); - // reuse the existing multiplexer - ++ i->second.m_iRefCount; - s->m_pUDT->m_pSndQueue = i->second.m_pSndQueue; - s->m_pUDT->m_pRcvQueue = i->second.m_pRcvQueue; - s->m_iMuxID = i->second.m_iID; - s->m_SelfAddr.family(addr.family()); - return; + HLOGC(smlog.Debug, log << "bind: muxer @" << m.m_iID << " found, but for port " + << m.m_iPort << " (requested port: " << port << ")"); + continue; } - } - } - } + + // If this is bound to the wildcard address, it can be reused if: + // - addr is also a wildcard + // - channel settings match + // Otherwise it's a conflict. + sockaddr_any sa; + m.m_pChannel->getSockAddr((sa)); + + HLOGC(smlog.Debug, log << "bind: Found existing muxer @" << m.m_iID << " : " << sa.str() + << " - check against " << addr.str()); + + if (sa.isany()) + { + if (!addr.isany()) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with existing wildcard binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + + // Still, for ANY you need either the same family, or open + // for families. + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->m_pUDT->m_config.iIpV6Only) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with existing IPv6 wildcard binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + + if ((m.m_mcfg.iIpV6Only == 0 || s->m_pUDT->m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + { + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with IPv6 wildcard binding: " << sa.str() + << " : family " << (m.m_iIPversion == AF_INET ? "IPv4" : "IPv6") + << " vs. " << (addr.family() == AF_INET ? "IPv4" : "IPv6")); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + reuse_attempt = true; + HLOGC(smlog.Debug, log << "bind: wildcard address - multiplexer reusable"); + } + else if (addr.isany() && addr.family() == sa.family()) + { + LOGC(smlog.Error, log << "bind: Wildcard address: " << addr.str() + << " conflicts with existting IP binding: " << sa.str()); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + // If this is bound to a certain address, AND: + else if (sa.equal_address(addr)) + { + // - the address is the same as addr + reuse_attempt = true; + HLOGC(smlog.Debug, log << "bind: same IP address - multiplexer reusable"); + } + else + { + HLOGC(smlog.Debug, log << "bind: IP addresses differ - ALLOWED to create a new multiplexer"); + } + // Otherwise: + // - the address is different than addr + // - the address can't be reused, but this can go on with new one. + + // If this is a reusage attempt: + if (reuse_attempt) + { + // - if the channel settings match, it can be reused + if (channelSettingsMatch(m, s)) + { + HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port); + // reuse the existing multiplexer + ++ i->second.m_iRefCount; + installMuxer((s), (i->second)); + return; + } + else + { + // - if not, it's a conflict + LOGC(smlog.Error, log << "bind: Address: " << addr.str() + << " conflicts with binding: " << sa.str() << " due to channel settings"); + throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); + } + } + // If not, proceed to the next one, and when there are no reusage + // candidates, proceed with creating a new multiplexer. + + // Note that a binding to a different IP address is not treated + // as a candidate for either reuseage or conflict. + } + } // a new multiplexer is needed CMultiplexer m; - m.m_mcfg = s->m_pUDT->m_config; - m.m_iIPversion = addr.family(); - m.m_iRefCount = 1; - m.m_iID = s->m_SocketID; + configureMuxer((m), s, addr.family()); try { @@ -2903,13 +3002,7 @@ void srt::CUDTUnited::updateMux( m.m_pChannel->open(addr); } - sockaddr_any sa; - m.m_pChannel->getSockAddr((sa)); - m.m_iPort = sa.hport(); - s->m_SelfAddr = sa; // Will be also completed later, but here it's needed for later checks - m.m_pTimer = new CTimer; - m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; @@ -2917,11 +3010,10 @@ void srt::CUDTUnited::updateMux( 32, s->m_pUDT->maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + // Rewrite the port here, as it might be only known upon return + // from CChannel::open. + m.m_iPort = installMuxer((s), m); m_mMultiplexer[m.m_iID] = m; - - s->m_pUDT->m_pSndQueue = m.m_pSndQueue; - s->m_pUDT->m_pRcvQueue = m.m_pRcvQueue; - s->m_iMuxID = m.m_iID; } catch (const CUDTException&) { @@ -2934,8 +3026,7 @@ void srt::CUDTUnited::updateMux( throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0); } - HLOGF(smlog.Debug, - "creating new multiplexer for port %i\n", m.m_iPort); + HLOGC(smlog.Debug, log << "bind: creating new multiplexer for port " << m.m_iPort); } // This function is going to find a multiplexer for the port contained diff --git a/srtcore/api.h b/srtcore/api.h index 3ef75c6e7..4228d8b46 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -408,6 +408,11 @@ friend class CRendezvousQueue; void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); + // Utility functions for updateMux + void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); + uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm); + bool channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s); + private: std::map m_mMultiplexer; // UDP multiplexer sync::Mutex m_MultiplexerLock; diff --git a/srtcore/srt.h b/srtcore/srt.h index 21e94879e..5d3349a48 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -473,6 +473,7 @@ enum CodeMinor MN_XSIZE = 12, MN_EIDINVAL = 13, MN_EEMPTY = 14, + MN_BUSYPORT = 15, // MJ_AGAIN MN_WRAVAIL = 1, MN_RDAVAIL = 2, @@ -528,6 +529,7 @@ typedef enum SRT_ERRNO SRT_ELARGEMSG = MN(NOTSUP, XSIZE), SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL), SRT_EPOLLEMPTY = MN(NOTSUP, EEMPTY), + SRT_EBINDCONFLICT = MN(NOTSUP, BUSYPORT), SRT_EASYNCFAIL = MJ(AGAIN), SRT_EASYNCSND = MN(AGAIN, WRAVAIL), diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index c37ee276d..7393c1279 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -19,7 +19,6 @@ const char* strerror_msgs_success [] = { "" }; - // MJ_SETUP 'Connection setup failure' const char* strerror_msgs_setup [] = { @@ -32,7 +31,6 @@ const char* strerror_msgs_setup [] = { "" }; - // MJ_CONNECTION '' const char* strerror_msgs_connection [] = { @@ -42,7 +40,6 @@ const char* strerror_msgs_connection [] = { "" }; - // MJ_SYSTEMRES 'System resource failure' const char* strerror_msgs_systemres [] = { @@ -53,7 +50,6 @@ const char* strerror_msgs_systemres [] = { "" }; - // MJ_FILESYSTEM 'File system failure' const char* strerror_msgs_filesystem [] = { @@ -65,7 +61,6 @@ const char* strerror_msgs_filesystem [] = { "" }; - // MJ_NOTSUP 'Operation not supported' const char* strerror_msgs_notsup [] = { @@ -84,10 +79,10 @@ const char* strerror_msgs_notsup [] = { "Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12 "Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13 "Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14 + "Operation not supported: Another socket is bound to that port and is not reusable for requested settings", // MN_BUSYPORT = 15 "" }; - // MJ_AGAIN 'Non-blocking call failure' const char* strerror_msgs_again [] = { @@ -99,7 +94,6 @@ const char* strerror_msgs_again [] = { "" }; - // MJ_PEERERROR 'The peer side has signaled an error' const char* strerror_msgs_peererror [] = { @@ -108,7 +102,6 @@ const char* strerror_msgs_peererror [] = { }; - const char** strerror_array_major [] = { strerror_msgs_success, // MJ_SUCCESS = 0 strerror_msgs_setup, // MJ_SETUP = 1 @@ -123,7 +116,7 @@ const char** strerror_array_major [] = { #define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0]) -const size_t strerror_array_sizes[] = { +const size_t strerror_array_sizes [] = { SRT_ARRAY_SIZE(strerror_msgs_success) - 1, SRT_ARRAY_SIZE(strerror_msgs_setup) - 1, SRT_ARRAY_SIZE(strerror_msgs_connection) - 1, @@ -135,6 +128,7 @@ const size_t strerror_array_sizes[] = { 0 }; + const char* strerror_get_message(size_t major, size_t minor) { static const char* const undefined = "UNDEFINED ERROR"; diff --git a/test/filelist.maf b/test/filelist.maf index 77f0d4b1f..d92cabd9d 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -20,6 +20,7 @@ test_sync.cpp test_timer.cpp test_unitqueue.cpp test_utilities.cpp +test_reuseaddr.cpp # Tests for bonding only - put here! diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp new file mode 100644 index 000000000..18ab5fdfa --- /dev/null +++ b/test/test_reuseaddr.cpp @@ -0,0 +1,440 @@ +#include "gtest/gtest.h" +#include +#ifndef _WIN32 +#include +#endif + +#include "common.h" +#include "srt.h" +#include "udt.h" + +// Copied from ../apps/apputil.cpp, can't really link this file here. +sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_family = AF_INET) +{ + using namespace std; + + // Handle empty name. + // If family is specified, empty string resolves to ANY of that family. + // If not, it resolves to IPv4 ANY (to specify IPv6 any, use [::]). + if (name == "") + { + sockaddr_any result(pref_family == AF_INET6 ? pref_family : AF_INET); + result.hport(port); + return result; + } + + bool first6 = pref_family != AF_INET; + int families[2] = {AF_INET6, AF_INET}; + if (!first6) + { + families[0] = AF_INET; + families[1] = AF_INET6; + } + + for (int i = 0; i < 2; ++i) + { + int family = families[i]; + sockaddr_any result (family); + + // Try to resolve the name by pton first + if (inet_pton(family, name.c_str(), result.get_addr()) == 1) + { + result.hport(port); // same addr location in ipv4 and ipv6 + return result; + } + } + + // If not, try to resolve by getaddrinfo + // This time, use the exact value of pref_family + + sockaddr_any result; + addrinfo fo = { + 0, + pref_family, + 0, 0, + 0, 0, + NULL, NULL + }; + + addrinfo* val = nullptr; + int erc = getaddrinfo(name.c_str(), nullptr, &fo, &val); + if (erc == 0) + { + result.set(val->ai_addr); + result.len = result.size(); + result.hport(port); // same addr location in ipv4 and ipv6 + } + freeaddrinfo(val); + + return result; +} + +#ifdef _WIN32 + +// On Windows there's a function for it, but it requires an extra +// iphlp library to be attached to the executable, which is kinda +// problematic. Temporarily block tests using this function on Windows. + +std::string GetLocalIP() +{ + std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n"; + return ""; +} +#else +struct IfAddr +{ + ifaddrs* handle; + IfAddr() + { + getifaddrs(&handle); + } + + ~IfAddr() + { + freeifaddrs(handle); + } +}; + +std::string GetLocalIP() +{ + struct ifaddrs * ifa=NULL; + void * tmpAddrPtr=NULL; + + IfAddr ifAddr; + + for (ifa = ifAddr.handle; ifa != NULL; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) + { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) + { + // is a valid IP4 Address + sockaddr_in* psin = (struct sockaddr_in *)ifa->ifa_addr; + tmpAddrPtr=&psin->sin_addr; + + if (ntohl(psin->sin_addr.s_addr) == 0x7f000001) // Skip 127.0.0.1 + continue; + + char addressBuffer[INET_ADDRSTRLEN] = ""; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + return addressBuffer; + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 + // is a valid IP6 Address + tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN] = ""; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + return addressBuffer; + } + } + + return ""; +} +#endif + +int client_pollid = SRT_ERROR; +SRTSOCKET m_client_sock = SRT_ERROR; + +void clientSocket(std::string ip, int port, bool expect_success) +{ + int yes = 1; + int no = 0; + + int family = AF_INET; + if (ip.substr(0, 2) == "6.") + { + family = AF_INET6; + ip = ip.substr(2); + } + + m_client_sock = srt_create_socket(); + ASSERT_NE(m_client_sock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + + int epoll_out = SRT_EPOLL_OUT; + srt_epoll_add_usock(client_pollid, m_client_sock, &epoll_out); + + sockaddr_any sa = CreateAddr(ip, port, family); + + std::cout << "Connecting to: " << sa.str() << std::endl; + + int connect_res = srt_connect(m_client_sock, sa.get(), sa.size()); + + if (connect_res == -1) + { + std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl; + } + + if (expect_success) + { + EXPECT_NE(connect_res, -1); + // Socket readiness for connection is checked by polling on WRITE allowed sockets. + + { + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR); + + + EXPECT_EQ(rlen, 0); // get exactly one write event without reads + EXPECT_EQ(wlen, 1); // get exactly one write event without reads + EXPECT_EQ(write[0], m_client_sock); // for our client socket + } + + char buffer[1316] = {1, 2, 3, 4}; + EXPECT_NE(srt_sendmsg(m_client_sock, buffer, sizeof buffer, + -1, // infinit ttl + true // in order must be set to true + ), + SRT_ERROR); + } + else + { + EXPECT_EQ(connect_res, -1); + } + + std::cout << "Client exit\n"; +} + +int server_pollid = SRT_ERROR; + +void serverSocket(std::string ip, int port, bool expect_success) +{ + int yes = 1; + int no = 0; + + SRTSOCKET m_bindsock = srt_create_socket(); + ASSERT_NE(m_bindsock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + + int epoll_in = SRT_EPOLL_IN; + srt_epoll_add_usock(server_pollid, m_bindsock, &epoll_in); + + sockaddr_any sa = CreateAddr(ip, port); + + std::cout << "Bind to: " << sa.str() << std::endl; + + int bind_res = srt_bind(m_bindsock, sa.get(), sa.size()); + if (!expect_success) + { + std::cout << "Binding should fail: " << srt_getlasterror_str() << std::endl; + ASSERT_EQ(bind_res, SRT_ERROR); + return; + } + + ASSERT_NE(bind_res, SRT_ERROR); + + ASSERT_NE(srt_listen(m_bindsock, SOMAXCONN), SRT_ERROR); + + if (ip == "0.0.0.0") + ip = "127.0.0.1"; // override wildcard + else if ( ip == "::") + ip = "::1"; + + std::thread client(clientSocket, ip, port, expect_success); + + { // wait for connection from client + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + ASSERT_NE(srt_epoll_wait(server_pollid, + read, &rlen, + write, &wlen, + 3000, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR ); + + + ASSERT_EQ(rlen, 1); // get exactly one read event without writes + ASSERT_EQ(wlen, 0); // get exactly one read event without writes + ASSERT_EQ(read[0], m_bindsock); // read event is for bind socket + } + + sockaddr_any scl; + + SRTSOCKET m_sock = srt_accept(m_bindsock, scl.get(), &scl.len); + if (m_sock == -1) + { + std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; + } + ASSERT_NE(m_sock, SRT_INVALID_SOCK); + + sockaddr_any showacp = (sockaddr*)&scl; + std::cout << "Accepted from: " << showacp.str() << std::endl; + + srt_epoll_add_usock(server_pollid, m_sock, &epoll_in); // wait for input + + char buffer[1316]; + { // wait for 1316 packet from client + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + ASSERT_NE(srt_epoll_wait(server_pollid, + read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR ); + + + ASSERT_EQ(rlen, 1); // get exactly one read event without writes + ASSERT_EQ(wlen, 0); // get exactly one read event without writes + ASSERT_EQ(read[0], m_sock); // read event is for bind socket + } + + char pattern[4] = {1, 2, 3, 4}; + + ASSERT_EQ(srt_recvmsg(m_sock, buffer, sizeof buffer), + 1316); + + EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); + + client.join(); + srt_close(m_sock); + srt_close(m_bindsock); + srt_close(m_client_sock); // cannot close m_client_sock after srt_sendmsg because of issue in api.c:2346 + + std::cout << "Server exit\n"; +} + +TEST(ReuseAddr, SameAddr1) +{ + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + std::thread server_1(serverSocket, "127.0.0.1", 5000, true); + server_1.join(); + + std::thread server_2(serverSocket, "127.0.0.1", 5000, true); + server_2.join(); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, SameAddr2) +{ + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + std::thread server_1(serverSocket, localip, 5000, true); + server_1.join(); + + std::thread server_2(serverSocket, localip, 5000, true); + server_2.join(); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, DiffAddr) +{ + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("127.0.0.1", 5000, true); + serverSocket(localip, 5000, true); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + +TEST(ReuseAddr, Wildcard) +{ +#if defined(_WIN32) || defined(CYGWIN) + std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" + "Forcing test to pass, PLEASE FIX.\n"; + return; +#endif + std::string localip = GetLocalIP(); + if (localip == "") + return; // DISABLE TEST if this doesn't work. + + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("0.0.0.0", 5000, true); + serverSocket(localip, 5000, false); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} + + +TEST(ReuseAddr, ProtocolVersion) +{ +#if defined(_WIN32) || defined(CYGWIN) + std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" + "Forcing test to pass, PLEASE FIX.\n"; + return; +#endif + ASSERT_EQ(srt_startup(), 0); + + client_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, client_pollid); + + server_pollid = srt_epoll_create(); + ASSERT_NE(SRT_ERROR, server_pollid); + + serverSocket("::", 5000, true); + serverSocket("0.0.0.0", 5000, true); + + (void)srt_epoll_release(client_pollid); + (void)srt_epoll_release(server_pollid); + srt_cleanup(); +} diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 950af154a..4967fbf86 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -449,8 +449,8 @@ TEST(SyncEvent, WaitForTwoNotifyOne) using wait_t = decltype(future_t().wait_for(chrono::microseconds(0))); wait_t wait_state[2] = { - move(future_result[0].wait_for(chrono::microseconds(100))), - move(future_result[1].wait_for(chrono::microseconds(100))) + move(future_result[0].wait_for(chrono::microseconds(500))), + move(future_result[1].wait_for(chrono::microseconds(500))) }; cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size() From 17fee159f9089e84cb2115747ba89c8d9ea7386a Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Mon, 31 May 2021 19:45:33 +0800 Subject: [PATCH 085/683] [core] make sure TTL will not drop packets over last block (#2005) --- srtcore/buffer.cpp | 11 +++++++++-- srtcore/buffer.h | 2 +- srtcore/core.cpp | 7 ------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 5a756792e..db3102e7a 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -522,8 +522,15 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: // XXX Suboptimal procedure to keep the blocks identifiable // by sequence number. Consider using some circular buffer. - for (int i = 0; i < offset; ++i) + for (int i = 0; i < offset && p != m_pLastBlock; ++i) + { p = p->m_pNext; + } + if (p == m_pLastBlock) + { + LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); + return 0; + } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; @@ -550,7 +557,7 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: w_msglen = 1; p = p->m_pNext; bool move = false; - while (msgno == p->getMsgSeq()) + while (p != m_pLastBlock && msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9a92e25b0..f69bef1d9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -153,7 +153,7 @@ class CSndBuffer /// @param [out] msgno message number of the packet. /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message - /// @return Actual length of data read. + /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// Get the time of the last retransmission (if any) of the DATA packet. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1e9d2f052..826e9c18d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8882,7 +8882,6 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi int msglen; const int payload = m_pSndBuffer->readData(offset, (w_packet), (w_origintime), (msglen)); - SRT_ASSERT(payload != 0); if (payload == -1) { int32_t seqpair[2]; @@ -8903,12 +8902,6 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi continue; } - // NOTE: This is just a sanity check. Returning 0 is impossible to happen - // in case of retransmission. If the offset was a positive value, then the - // block must exist in the old blocks because it wasn't yet cut off by ACK - // and has been already recorded as sent (otherwise the peer wouldn't send - // back the loss report). May something happen here in case when the send - // loss record has been updated by the FASTREXMIT. else if (payload == 0) continue; From 1751babcbe7758ec515d2ec6f2d1bac0f5e94991 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 31 May 2021 16:04:02 +0200 Subject: [PATCH 086/683] [core] FIX: Added check for NULL unit when passing to updateConnStatus (#2028) --- srtcore/core.cpp | 48 +++++++++++++++++++++++++++++------------------ srtcore/core.h | 6 +++--- srtcore/queue.cpp | 20 ++++++++++++-------- srtcore/queue.h | 2 +- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 826e9c18d..90162fa89 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3600,7 +3600,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // it means that it has done all that was required, however none of the below // things has to be done (this function will do it by itself if needed). // Otherwise the handshake rolling can be interrupted and considered complete. - cst = processRendezvous(response, serv_addr, RST_OK, (reqpkt)); + cst = processRendezvous(&response, serv_addr, RST_OK, (reqpkt)); if (cst == CONN_CONTINUE) continue; break; @@ -3744,7 +3744,7 @@ EConnectStatus srt::CUDT::processAsyncConnectResponse(const CPacket &pkt) ATR_NO bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, - const CPacket& response, + const CPacket* pResponse /*[[nullable]]*/, const sockaddr_any& serv_addr) { // IMPORTANT! @@ -3773,7 +3773,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(response, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (request)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3976,7 +3976,7 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas } EConnectStatus srt::CUDT::processRendezvous( - const CPacket& response, const sockaddr_any& serv_addr, + const CPacket* pResponse /*[[nullable]]*/, const sockaddr_any& serv_addr, EReadStatus rst, CPacket& w_reqpkt) { if (m_RdvState == CHandShake::RDV_CONNECTED) @@ -4053,7 +4053,7 @@ EConnectStatus srt::CUDT::processRendezvous( // We have JUST RECEIVED packet in this session (not that this is called as periodic update). // Sanity check m_tsLastReqTime = steady_clock::time_point(); - if (response.getLength() == size_t(-1)) + if (!pResponse || pResponse->getLength() == size_t(-1)) { m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Fatal, @@ -4061,7 +4061,7 @@ EConnectStatus srt::CUDT::processRendezvous( return CONN_REJECT; } - if (!interpretSrtHandshake(m_ConnRes, response, kmdata, &kmdatasize)) + if (!interpretSrtHandshake(m_ConnRes, *pResponse, kmdata, &kmdatasize)) { HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in interpretSrtHandshake REQ-TIME: LOW."); @@ -4114,7 +4114,7 @@ EConnectStatus srt::CUDT::processRendezvous( // not be done in case of rendezvous. The section in postConnect() is // predicted to run only in regular CALLER handling. - if (rst != RST_OK || response.getLength() == size_t(-1)) + if (rst != RST_OK || !pResponse || pResponse->getLength() == size_t(-1)) { // Actually the -1 length would be an IPE, but it's likely that this was reported already. HLOGC( @@ -4125,7 +4125,7 @@ EConnectStatus srt::CUDT::processRendezvous( { HLOGC(cnlog.Debug, log << "processRendezvous: INITIATOR, will send AGREEMENT - interpreting HSRSP extension"); - if (!interpretSrtHandshake(m_ConnRes, response, 0, 0)) + if (!interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0)) { // m_RejectReason is already set, so set the reqtype accordingly m_ConnReq.m_iReqType = URQFailure(m_RejectReason); @@ -4165,10 +4165,7 @@ EConnectStatus srt::CUDT::processRendezvous( w_reqpkt.setLength(m_iMaxSRTPayloadSize); if (m_RdvState == CHandShake::RDV_CONNECTED) { - // When synchro=false, don't lock a mutex for rendezvous queue. - // This is required when this function is called in the - // receive queue worker thread - it would lock itself. - int cst = postConnect(response, true, 0); + int cst = postConnect(pResponse, true, 0); if (cst == CONN_REJECT) { // m_RejectReason already set @@ -4304,7 +4301,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx m_RdvState = CHandShake::RDV_CONNECTED; } - return postConnect(response, hsv5, eout); + return postConnect(&response, hsv5, eout); } if (!response.isControl(UMSG_HANDSHAKE)) @@ -4474,7 +4471,7 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx } } - return postConnect(response, false, eout); + return postConnect(&response, false, eout); } bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT @@ -4507,7 +4504,7 @@ bool srt::CUDT::applyResponseSettings() ATR_NOEXCEPT return true; } -EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT +EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT { if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. @@ -4516,6 +4513,21 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // in rendezvous it's completed before calling this function. if (!rendezvous) { + // The "local storage depleted" case shouldn't happen here, but + // this is a theoretical path that needs prevention. + bool ok = pResponse; + if (!ok) + { + m_RejectReason = SRT_REJ_IPE; + if (eout) + { + *eout = CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + return CONN_REJECT; + } + + // [[assert (pResponse != NULL)]]; + // NOTE: THIS function must be called before calling prepareConnectionObjects. // The reason why it's not part of prepareConnectionObjects is that the activities // done there are done SIMILAR way in acceptAndRespond, which also calls this @@ -4527,7 +4539,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // // Currently just this function must be called always BEFORE prepareConnectionObjects // everywhere except acceptAndRespond(). - bool ok = applyResponseSettings(); + ok = applyResponseSettings(); // This will actually be done also in rendezvous HSv4, // however in this case the HSREQ extension will not be attached, @@ -4537,8 +4549,8 @@ EConnectStatus srt::CUDT::postConnect(const CPacket &response, bool rendezvous, // May happen that 'response' contains a data packet that was sent in rendezvous mode. // In this situation the interpretation of handshake was already done earlier. - ok = ok && response.isControl(); - ok = ok && interpretSrtHandshake(m_ConnRes, response, 0, 0); + ok = ok && pResponse->isControl(); + ok = ok && interpretSrtHandshake(m_ConnRes, *pResponse, 0, 0); if (!ok) { diff --git a/srtcore/core.h b/srtcore/core.h index 2e463423c..eed601ea0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -475,12 +475,12 @@ class CUDT /// @param response incoming handshake response packet to be interpreted /// @param serv_addr incoming packet's address /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) - SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket &response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); - SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; - SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr); + SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr); SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index fe323ab4c..9be4af013 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -903,17 +903,21 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& return NULL; } -void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn) +void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst, CUnit* unit) { vector toRemove, toProcess; + const CPacket* pkt = unit ? &unit->m_Packet : NULL; + + // Need a stub value for a case when there's no unit provided ("storage depleted" case). + // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. + const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. - if (!qualifyToHandle(rst, cst, pktIn.m_iID, toRemove, toProcess)) + if (!qualifyToHandle(rst, cst, dest_id, (toRemove), (toProcess))) return; - // [[using locked()]]; - HLOGC(cnlog.Debug, log << "updateConnStatus: collected " << toProcess.size() << " for processing, " << toRemove.size() << " to close"); @@ -938,7 +942,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (i->id != pktIn.m_iID) + if (i->id != dest_id) { read_st = RST_AGAIN; conn_st = CONN_AGAIN; @@ -947,7 +951,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst HLOGC(cnlog.Debug, log << "updateConnStatus: processing async conn for @" << i->id << " FROM " << i->peeraddr.str()); - if (!i->u->processAsyncConnectRequest(read_st, conn_st, pktIn, i->peeraddr)) + if (!i->u->processAsyncConnectRequest(read_st, conn_st, pkt, i->peeraddr)) { // cst == CONN_REJECT can only be result of worker_ProcessAddressedPacket and // its already set in this case. @@ -1310,7 +1314,7 @@ void* srt::CRcvQueue::worker(void* param) // worker_TryAsyncRend_OrStore ---> // CUDT::processAsyncConnectResponse ---> // CUDT::processConnectResponse - self->m_pRendezvousQueue->updateConnStatus(rst, cst, unit->m_Packet); + self->m_pRendezvousQueue->updateConnStatus(rst, cst, unit); // XXX updateConnStatus may have removed the connector from the list, // however there's still m_mBuffer in CRcvQueue for that socket to care about. @@ -1526,7 +1530,7 @@ EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* un { LOGC(cnlog.Warn, log << "AsyncOrRND: PACKET NOT HANDSHAKE - re-requesting handshake from peer"); storePkt(id, unit->m_Packet.clone()); - if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, unit->m_Packet, u->m_PeerAddr)) + if (!u->processAsyncConnectRequest(RST_AGAIN, CONN_CONTINUE, &unit->m_Packet, u->m_PeerAddr)) { // Reuse previous behavior to reject a packet cst = CONN_REJECT; diff --git a/srtcore/queue.h b/srtcore/queue.h index b2ccb36d8..56afff9b6 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -354,7 +354,7 @@ class CRendezvousQueue /// @param rst result of reading from a UDP socket: received packet / nothin read / read error. /// @param cst target status for pending connection: reject or proceed. /// @param pktIn packet received from the UDP socket. - void updateConnStatus(EReadStatus rst, EConnectStatus cst, const CPacket& pktIn); + void updateConnStatus(EReadStatus rst, EConnectStatus cst, CUnit* unit); private: struct LinkStatusInfo From e37f4abc017f717220ccb624db41aa0fa03e2349 Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Tue, 1 Jun 2021 18:43:22 +1000 Subject: [PATCH 087/683] [build] Set openssl vars explicitly; Support mbedtls Android build (#2030) --- scripts/build-android/build-android | 48 ++++++++++++++++++++++------- scripts/build-android/mkmbedtls | 27 ++++++++++++++++ scripts/build-android/mksrt | 11 +++++-- 3 files changed, 73 insertions(+), 13 deletions(-) create mode 100755 scripts/build-android/mkmbedtls diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 622f525d2..3d69dac0c 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -6,9 +6,11 @@ echo_help() echo " -n Specify NDK root path for the build." echo " -a Select target API level." echo " -t Select target architectures." - echo " Android supports the following architectures: armeabi armeabi-v7a arm64-v8a x86 x86_64." + echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64." + echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" - echo " -s Select a specific SRT tag. E.g. v1.4.3" + echo " -m Select Mbed TLS version. E.g. v2.26.0" + echo " -s Select SRT version. E.g. v1.4.3" echo echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" echo @@ -17,11 +19,13 @@ echo_help() # Init optional command line vars NDK_ROOT="" API_LEVEL=28 -BUILD_TARGETS="armeabi armeabi-v7a arm64-v8a x86 x86_64" +BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" OPENSSL_VERSION=1.1.1h SRT_VERSION="" +ENC_LIB=openssl +MBEDTLS_VERSION=v2.26.0 -while getopts n:a:t:o:s: option +while getopts n:a:t:o:s:e:m: option do case "${option}" in @@ -30,6 +34,8 @@ do t) BUILD_TARGETS=${OPTARG};; o) OPENSSL_VERSION=${OPTARG};; s) SRT_VERSION=${OPTARG};; + e) ENC_LIB=${OPTARG};; + m) MBEDTLS_VERSION=${OPTARG};; *) twentytwo=${OPTARG};; esac done @@ -49,7 +55,19 @@ fi # Determine the path of the executing script BASE_DIR=$(readlink -f $0 | xargs dirname) -$BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION +if [ $ENC_LIB = 'openssl' ]; then + $BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION +elif [ $ENC_LIB = 'mbedtls' ]; then + if [ ! -d $BASE_DIR/mbedtls ]; then + git clone https://github.com/ARMmbed/mbedtls mbedtls + if [ ! -z "$MBEDTLS_VERSION" ]; then + git -C $BASE_DIR/mbedtls checkout $MBEDTLS_VERSION + fi + fi +else + echo "Unknown encryption library. Possible options: openssl mbedtls" + exit 128 +fi if [ ! -d $BASE_DIR/srt ]; then git clone https://github.com/Haivision/srt srt @@ -58,12 +76,20 @@ if [ ! -d $BASE_DIR/srt ]; then fi fi -JNI_DIR=$BASE_DIR/prebuilt - for build_target in $BUILD_TARGETS; do - git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/srt -i $BASE_DIR/$build_target + LIB_DIR=$BASE_DIR/$build_target/lib + JNI_DIR=$BASE_DIR/prebuilt/$build_target + + mkdir -p $JNI_DIR - mkdir -p $JNI_DIR/$build_target - cp $BASE_DIR/$build_target/lib/libsrt.so $JNI_DIR/$build_target/libsrt.so + if [ $ENC_LIB = 'mbedtls' ]; then + $BASE_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target + cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so + cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so + cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so + fi + + git -C $BASE_DIR/srt clean -fd + $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done diff --git a/scripts/build-android/mkmbedtls b/scripts/build-android/mkmbedtls new file mode 100755 index 000000000..176154184 --- /dev/null +++ b/scripts/build-android/mkmbedtls @@ -0,0 +1,27 @@ +#!/bin/sh + +while getopts s:i:t:n:a: option +do + case "${option}" + in + s) SRC_DIR=${OPTARG};; + i) INSTALL_DIR=${OPTARG};; + t) ARCH_ABI=${OPTARG};; + n) NDK_ROOT=${OPTARG};; + a) API_LEVEL=${OPTARG};; + *) twentytwo=${OPTARG};; + esac +done + + +BUILD_DIR=/tmp/mbedtls_android_build +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cd $BUILD_DIR +cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On \ +-DCMAKE_PREFIX_PATH=$INSTALL_DIR -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCMAKE_ANDROID_NDK=$NDK_ROOT \ +-DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$API_LEVEL -DCMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ +-DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ +-DCMAKE_BUILD_TYPE=RelWithDebInfo $SRC_DIR +cmake --build . +cmake --install . diff --git a/scripts/build-android/mksrt b/scripts/build-android/mksrt index 2af96a7aa..b2e176046 100755 --- a/scripts/build-android/mksrt +++ b/scripts/build-android/mksrt @@ -1,6 +1,6 @@ #!/bin/sh -while getopts s:i:t:n:a: option +while getopts s:i:t:n:a:e: option do case "${option}" in @@ -9,13 +9,20 @@ do t) ARCH_ABI=${OPTARG};; n) NDK_ROOT=${OPTARG};; a) API_LEVEL=${OPTARG};; + e) ENC_LIB=${OPTARG};; *) twentytwo=${OPTARG};; esac done cd $SRC_DIR -./configure --use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=true \ +./configure --use-enclib=$ENC_LIB \ +--use-openssl-pc=OFF --OPENSSL_USE_STATIC_LIBS=TRUE \ +--OPENSSL_INCLUDE_DIR=$INSTALL_DIR/include \ +--OPENSSL_CRYPTO_LIBRARY=$INSTALL_DIR/lib/libcrypto.a --OPENSSL_SSL_LIBRARY=$INSTALL_DIR/lib/libssl.a \ +--STATIC_MBEDTLS=FALSE \ +--MBEDTLS_INCLUDE_DIR=$INSTALL_DIR/include --MBEDTLS_INCLUDE_DIRS=$INSTALL_DIR/include \ +--MBEDTLS_LIBRARIES=$INSTALL_DIR/lib/libmbedtls.so \ --CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \ --CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \ --CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \ From 0f0caf948733033915f09914c907fe5e1ecc375b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 1 Jun 2021 15:08:17 +0200 Subject: [PATCH 088/683] [core] Added atomic support and marked atomic key fields detected by thread sanitizer (#1863) --- srtcore/api.cpp | 45 +++++--- srtcore/api.h | 4 +- srtcore/atomic.h | 210 ++++++++++++++++++++++++++++++++++++ srtcore/atomic_msvc.h | 245 ++++++++++++++++++++++++++++++++++++++++++ srtcore/congctl.cpp | 4 +- srtcore/core.cpp | 114 +++++++++++--------- srtcore/core.h | 75 ++++++------- srtcore/group.h | 2 +- srtcore/queue.cpp | 6 +- srtcore/queue.h | 10 +- srtcore/sync.h | 74 +++++++++++++ 11 files changed, 677 insertions(+), 112 deletions(-) create mode 100644 srtcore/atomic.h create mode 100644 srtcore/atomic_msvc.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ad243d50e..aee13f389 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2641,13 +2641,17 @@ void srt::CUDTUnited::checkBrokenSockets() // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when // this function is called (isRcvDataReady also checks if the // available data is "ready to play"). - && s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable() - && (s->m_pUDT->m_iBrokenCounter -- > 0)) + && s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable()) { - // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): - // %d\n", i->first); - // if there is still data in the receiver buffer, wait longer - continue; + const int bc = s->m_pUDT->m_iBrokenCounter.load(); + if (bc > 0) + { + // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): + // %d\n", i->first); + // if there is still data in the receiver buffer, wait longer + s->m_pUDT->m_iBrokenCounter.store(bc - 1); + continue; + } } #if ENABLE_EXPERIMENTAL_BONDING @@ -2702,15 +2706,17 @@ void srt::CUDTUnited::checkBrokenSockets() // RcvUList const steady_clock::time_point now = steady_clock::now(); const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; - if ((closed_ago > seconds_from(1)) - && ((!j->second->m_pUDT->m_pRNode) - || !j->second->m_pUDT->m_pRNode->m_bOnList)) + if (closed_ago > seconds_from(1)) { - HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " - << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); + CRNode* rnode = j->second->m_pUDT->m_pRNode; + if (!rnode || !rnode->m_bOnList) + { + HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " + << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); - // HLOGF(smlog.Debug, "will unref socket: %d\n", j->first); - tbr.push_back(j->first); + // HLOGF(smlog.Debug, "will unref socket: %d\n", j->first); + tbr.push_back(j->first); + } } } @@ -2734,6 +2740,19 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* const s = i->second; + // The socket may be in the trashcan now, but could + // still be under processing in the sender/receiver worker + // threads. If that's the case, SKIP IT THIS TIME. The + // socket will be checked next time the GC rollover starts. + CSNode* sn = s->m_pUDT->m_pSNode; + if (sn && sn->m_iHeapLoc != -1) + return; + + CRNode* rn = s->m_pUDT->m_pRNode; + if (rn && rn->m_bOnList) + return; + + #if ENABLE_EXPERIMENTAL_BONDING if (s->m_GroupOf) { diff --git a/srtcore/api.h b/srtcore/api.h index 4228d8b46..5750c9db1 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -102,7 +102,7 @@ class CUDTSocket void construct(); - SRT_SOCKSTATUS m_Status; //< current socket state + srt::sync::atomic m_Status; //< current socket state /// Time when the socket is closed. /// When the socket is closed, it is not removed immediately from the list @@ -421,7 +421,7 @@ friend class CRendezvousQueue; CCache* m_pCache; // UDT network information cache private: - volatile bool m_bClosing; + srt::sync::atomic m_bClosing; sync::Mutex m_GCStopLock; sync::Condition m_GCStopCond; diff --git a/srtcore/atomic.h b/srtcore/atomic.h new file mode 100644 index 000000000..5cedd396d --- /dev/null +++ b/srtcore/atomic.h @@ -0,0 +1,210 @@ +//---------------------------------------------------------------------------- +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or distribute +// this software, either in source code form or as a compiled binary, for any +// purpose, commercial or non-commercial, and by any means. +// +// In jurisdictions that recognize copyright laws, the author or authors of +// this software dedicate any and all copyright interest in the software to the +// public domain. We make this dedication for the benefit of the public at +// large and to the detriment of our heirs and successors. We intend this +// dedication to be an overt act of relinquishment in perpetuity of all present +// and future rights to this software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +//----------------------------------------------------------------------------- + +// SRT Project information: +// This file was adopted from a Public Domain project from +// https://github.com/mbitsnbites/atomic +// Only namespaces were changed to adopt it for SRT project. + +#ifndef SRT_SYNC_ATOMIC_H_ +#define SRT_SYNC_ATOMIC_H_ + +// Macro for disallowing copying of an object. +#if __cplusplus >= 201103L +#define ATOMIC_DISALLOW_COPY(T) \ + T(const T&) = delete; \ + T& operator=(const T&) = delete; +#else +#define ATOMIC_DISALLOW_COPY(T) \ + T(const T&); \ + T& operator=(const T&); +#endif + +// A portable static assert. +#if __cplusplus >= 201103L +#define ATOMIC_STATIC_ASSERT(condition, message) \ + static_assert((condition), message) +#else +// Based on: http://stackoverflow.com/a/809465/5778708 +#define ATOMIC_STATIC_ASSERT(condition, message) \ + _impl_STATIC_ASSERT_LINE(condition, __LINE__) +#define _impl_PASTE(a, b) a##b +#ifdef __GNUC__ +#define _impl_UNUSED __attribute__((__unused__)) +#else +#define _impl_UNUSED +#endif +#define _impl_STATIC_ASSERT_LINE(condition, line) \ + typedef char _impl_PASTE( \ + STATIC_ASSERT_failed_, \ + line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED +#endif + +#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) +#define ATOMIC_USE_GCC_INTRINSICS +#elif defined(_MSC_VER) +#define ATOMIC_USE_MSVC_INTRINSICS +#include "atomic_msvc.h" +#elif __cplusplus >= 201103L +#define ATOMIC_USE_CPP11_ATOMIC +#include +#else +#error Unsupported compiler / system. +#endif + +namespace srt { +namespace sync { +template +class atomic { +public: + ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || + sizeof(T) == 8, + "Only types of size 1, 2, 4 or 8 are supported"); + + atomic() : value_(static_cast(0)) {} + + explicit atomic(const T value) : value_(value) {} + + /// @brief Performs an atomic increment operation (value + 1). + /// @returns The new value of the atomic object. + T operator++() { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::increment(&value_); +#else + return ++value_; +#endif + } + + /// @brief Performs an atomic decrement operation (value - 1). + /// @returns The new value of the atomic object. + T operator--() { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::decrement(&value_); +#else + return --value_; +#endif + } + + /// @brief Performs an atomic compare-and-swap (CAS) operation. + /// + /// The value of the atomic object is only updated to the new value if the + /// old value of the atomic object matches @c expected_val. + /// + /// @param expected_val The expected value of the atomic object. + /// @param new_val The new value to write to the atomic object. + /// @returns True if new_value was written to the atomic object. + bool compare_exchange(const T expected_val, const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + T e = expected_val; + return __atomic_compare_exchange_n( + &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + const T old_val = + msvc::interlocked::compare_exchange(&value_, new_val, expected_val); + return (old_val == expected_val); +#else + T e = expected_val; + return value_.compare_exchange_weak(e, new_val); +#endif + } + + /// @brief Performs an atomic set operation. + /// + /// The value of the atomic object is unconditionally updated to the new + /// value. + /// + /// @param new_val The new value to write to the atomic object. + void store(const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + (void)msvc::interlocked::exchange(&value_, new_val); +#else + value_.store(new_val); +#endif + } + + /// @returns the current value of the atomic object. + /// @note Be careful about how this is used, since any operations on the + /// returned value are inherently non-atomic. + T load() const { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + // TODO(m): Is there a better solution for MSVC? + return value_; +#else + return value_; +#endif + } + + /// @brief Performs an atomic exchange operation. + /// + /// The value of the atomic object is unconditionally updated to the new + /// value, and the old value is returned. + /// + /// @param new_val The new value to write to the atomic object. + /// @returns the old value. + T exchange(const T new_val) { +#if defined(ATOMIC_USE_GCC_INTRINSICS) + return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + return msvc::interlocked::exchange(&value_, new_val); +#else + return value_.exchange(new_val); +#endif + } + + T operator=(const T new_value) { + store(new_value); + return new_value; + } + + operator T() const { + return load(); + } + +private: +#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) + volatile T value_; +#else + std::atomic value_; +#endif + + ATOMIC_DISALLOW_COPY(atomic) +}; + +} // namespace sync +} // namespace srt + +// Undef temporary defines. +#undef ATOMIC_USE_GCC_INTRINSICS +#undef ATOMIC_USE_MSVC_INTRINSICS +#undef ATOMIC_USE_CPP11_ATOMIC + +#endif // ATOMIC_ATOMIC_H_ diff --git a/srtcore/atomic_msvc.h b/srtcore/atomic_msvc.h new file mode 100644 index 000000000..9e4df2dbd --- /dev/null +++ b/srtcore/atomic_msvc.h @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or distribute +// this software, either in source code form or as a compiled binary, for any +// purpose, commercial or non-commercial, and by any means. +// +// In jurisdictions that recognize copyright laws, the author or authors of +// this software dedicate any and all copyright interest in the software to the +// public domain. We make this dedication for the benefit of the public at +// large and to the detriment of our heirs and successors. We intend this +// dedication to be an overt act of relinquishment in perpetuity of all present +// and future rights to this software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +//----------------------------------------------------------------------------- + +// SRT Project information: +// This file was adopted from a Public Domain project from +// https://github.com/mbitsnbites/atomic +// Only namespaces were changed to adopt it for SRT project. + +#ifndef SRT_SYNC_ATOMIC_MSVC_H_ +#define SRT_SYNC_ATOMIC_MSVC_H_ + +// Define which functions we need (don't include ). +extern "C" { +short _InterlockedIncrement16(short volatile*); +long _InterlockedIncrement(long volatile*); +__int64 _InterlockedIncrement64(__int64 volatile*); + +short _InterlockedDecrement16(short volatile*); +long _InterlockedDecrement(long volatile*); +__int64 _InterlockedDecrement64(__int64 volatile*); + +char _InterlockedExchange8(char volatile*, char); +short _InterlockedExchange16(short volatile*, short); +long __cdecl _InterlockedExchange(long volatile*, long); +__int64 _InterlockedExchange64(__int64 volatile*, __int64); + +char _InterlockedCompareExchange8(char volatile*, char, char); +short _InterlockedCompareExchange16(short volatile*, short, short); +long __cdecl _InterlockedCompareExchange(long volatile*, long, long); +__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64); +}; + +// Define which functions we want to use as inline intriniscs. +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedIncrement16) + +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedDecrement16) + +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedCompareExchange8) +#pragma intrinsic(_InterlockedCompareExchange16) + +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedExchange8) +#pragma intrinsic(_InterlockedExchange16) + +#if defined(_M_X64) +#pragma intrinsic(_InterlockedIncrement64) +#pragma intrinsic(_InterlockedDecrement64) +#pragma intrinsic(_InterlockedCompareExchange64) +#pragma intrinsic(_InterlockedExchange64) +#endif // _M_X64 + +namespace srt { +namespace sync { +namespace msvc { +template +struct interlocked { +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + // There's no _InterlockedIncrement8(). + char old_val, new_val; + do { + old_val = static_cast(*x); + new_val = old_val + static_cast(1); + } while (_InterlockedCompareExchange8(reinterpret_cast(x), + new_val, + old_val) != old_val); + return static_cast(new_val); + } + + static inline T decrement(T volatile* x) { + // There's no _InterlockedDecrement8(). + char old_val, new_val; + do { + old_val = static_cast(*x); + new_val = old_val - static_cast(1); + } while (_InterlockedCompareExchange8(reinterpret_cast(x), + new_val, + old_val) != old_val); + return static_cast(new_val); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange8(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast(_InterlockedExchange8( + reinterpret_cast(x), static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + return static_cast( + _InterlockedIncrement16(reinterpret_cast(x))); + } + + static inline T decrement(T volatile* x) { + return static_cast( + _InterlockedDecrement16(reinterpret_cast(x))); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange16(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast( + _InterlockedExchange16(reinterpret_cast(x), + static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { + return static_cast( + _InterlockedIncrement(reinterpret_cast(x))); + } + + static inline T decrement(T volatile* x) { + return static_cast( + _InterlockedDecrement(reinterpret_cast(x))); + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast( + _InterlockedCompareExchange(reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { + return static_cast(_InterlockedExchange( + reinterpret_cast(x), static_cast(new_val))); + } +}; + +template +struct interlocked { + static inline T increment(T volatile* x) { +#if defined(_M_X64) + return static_cast( + _InterlockedIncrement64(reinterpret_cast(x))); +#else + // There's no _InterlockedIncrement64() for 32-bit x86. + __int64 old_val, new_val; + do { + old_val = static_cast<__int64>(*x); + new_val = old_val + static_cast<__int64>(1); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(new_val); +#endif // _M_X64 + } + + static inline T decrement(T volatile* x) { +#if defined(_M_X64) + return static_cast( + _InterlockedDecrement64(reinterpret_cast(x))); +#else + // There's no _InterlockedDecrement64() for 32-bit x86. + __int64 old_val, new_val; + do { + old_val = static_cast<__int64>(*x); + new_val = old_val - static_cast<__int64>(1); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(new_val); +#endif // _M_X64 + } + + static inline T compare_exchange(T volatile* x, + const T new_val, + const T expected_val) { + return static_cast(_InterlockedCompareExchange64( + reinterpret_cast(x), + static_cast(new_val), + static_cast(expected_val))); + } + + static inline T exchange(T volatile* x, const T new_val) { +#if defined(_M_X64) + return static_cast( + _InterlockedExchange64(reinterpret_cast(x), + static_cast(new_val))); +#else + // There's no _InterlockedExchange64 for 32-bit x86. + __int64 old_val; + do { + old_val = static_cast<__int64>(*x); + } while (_InterlockedCompareExchange64( + reinterpret_cast(x), new_val, old_val) != + old_val); + return static_cast(old_val); +#endif // _M_X64 + } +}; +} // namespace msvc +} // namespace sync +} // namespace srt + +#endif // ATOMIC_ATOMIC_MSVC_H_ diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 998256216..4612e6852 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -60,7 +60,7 @@ void SrtCongestion::Check() class LiveCC: public SrtCongestionControlBase { int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) - size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit + srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; // NAKREPORT stuff. @@ -167,7 +167,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod << " by avg pktsize=" << m_zSndAvgPayloadSize diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 90162fa89..17611191e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -254,7 +254,7 @@ void srt::CUDT::construct() // TODO: m_iBrokenCounter should be still set to some default. m_bPeerHealth = true; m_RejectReason = SRT_REJ_UNKNOWN; - m_tsLastReqTime = steady_clock::time_point(); + m_tsLastReqTime.store(steady_clock::time_point()); m_SrtHsSide = HSD_DRAW; m_uPeerSrtVersion = 0; // Not defined until connected. m_iTsbPdDelay_ms = 0; @@ -947,11 +947,11 @@ void srt::CUDT::open() m_tdNAKInterval = m_tdMinNakInterval; const steady_clock::time_point currtime = steady_clock::now(); - m_tsLastRspTime = currtime; - m_tsNextACKTime = currtime + m_tdACKInterval; - m_tsNextNAKTime = currtime + m_tdNAKInterval; - m_tsLastRspAckTime = currtime; - m_tsLastSndTime = currtime; + m_tsLastRspTime.store(currtime); + m_tsNextACKTime.store(currtime + m_tdACKInterval); + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); + m_tsLastRspAckTime = currtime; + m_tsLastSndTime.store(currtime); m_tsUnstableSince = steady_clock::time_point(); m_tsFreshActivation = steady_clock::time_point(); @@ -3524,7 +3524,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) while (!m_bClosing) { - const steady_clock::duration tdiff = steady_clock::now() - m_tsLastReqTime; + const steady_clock::duration tdiff = steady_clock::now() - m_tsLastReqTime.load(); // avoid sending too many requests, at most 1 request per 250ms // SHORT VERSION: @@ -5714,11 +5714,11 @@ SRT_REJECT_REASON srt::CUDT::setupCC() // Update timers const steady_clock::time_point currtime = steady_clock::now(); - m_tsLastRspTime = currtime; - m_tsNextACKTime = currtime + m_tdACKInterval; - m_tsNextNAKTime = currtime + m_tdNAKInterval; - m_tsLastRspAckTime = currtime; - m_tsLastSndTime = currtime; + m_tsLastRspTime.store(currtime); + m_tsNextACKTime.store(currtime + m_tdACKInterval); + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); + m_tsLastRspAckTime = currtime; + m_tsLastSndTime.store(currtime); HLOGC(rslog.Debug, log << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" << m_iFlowWindowSize @@ -6848,6 +6848,7 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (m_pSndBuffer->getCurrBufSize() == 0) { // delay the EXP timer to avoid mis-fired timeout + // XXX Lock ??? ScopedLock ack_lock(m_RecvAckLock); m_tsLastRspAckTime = steady_clock::now(); m_iReXmitCount = 1; } @@ -7151,8 +7152,8 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; - perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval); - perf->pktFlowWindow = m_iFlowWindowSize; + perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); + perf->pktFlowWindow = m_iFlowWindowSize.load(); perf->pktCongestionWindow = (int)m_dCongestionWindow; perf->pktFlightSize = getFlightSpan(); perf->msRTT = (double)m_iSRTT / 1000.0; @@ -7164,7 +7165,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) : 0; - const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth; + const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth.load(); perf->mbpsBandwidth = Bps2Mbps(availbw * (m_iMaxSRTPayloadSize + pktHdrSize)); @@ -7640,7 +7641,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp // Fix keepalive if (nbsent) - m_tsLastSndTime = steady_clock::now(); + m_tsLastSndTime.store(steady_clock::now()); } int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) @@ -8010,7 +8011,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) { ScopedLock ack_lock(m_RecvAckLock); - m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); + m_iFlowWindowSize = m_iFlowWindowSize - CSeqNo::seqoff(m_iSndLastAck, ackdata_seqno); m_iSndLastAck = ackdata_seqno; // TODO: m_tsLastRspAckTime should be protected with m_RecvAckLock @@ -8150,11 +8151,15 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + int crtt = m_iSRTT.load(), crttvar = m_iRTTVar.load(); + + if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); - m_iSRTT = avg_iir<8>(m_iSRTT, rtt); + crttvar = avg_iir<4>(crttvar, abs(crtt - crtt)); + crtt = avg_iir<8>(crtt, crtt); } + m_iSRTT = crtt; + m_iRTTVar = crttvar; } else // Transmission is unidirectional. { @@ -8212,9 +8217,9 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ else bytesps = pktps * m_iMaxSRTPayloadSize; - m_iBandwidth = avg_iir<8>(m_iBandwidth, bandwidth); - m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate, pktps); - m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate, bytesps); + m_iBandwidth = avg_iir<8>(m_iBandwidth.load(), bandwidth); + m_iDeliveryRate = avg_iir<8>(m_iDeliveryRate.load(), pktps); + m_iByteDeliveryRate = avg_iir<8>(m_iByteDeliveryRate.load(), bytesps); // Update Estimated Bandwidth and packet delivery rate // m_iRcvRate = m_iDeliveryRate; @@ -8272,8 +8277,8 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // on subsequent RTT samples (during transmission). if (m_bIsFirstRTTReceived) { - m_iRTTVar = avg_iir<4>(m_iRTTVar, abs(rtt - m_iSRTT)); - m_iSRTT = avg_iir<8>(m_iSRTT, rtt); + m_iRTTVar = avg_iir<4>(m_iRTTVar.load(), abs(rtt - m_iSRTT.load())); + m_iSRTT = avg_iir<8>(m_iSRTT.load(), rtt); } // Reset the value of smoothed RTT on the first RTT sample after initialization // (at the beginning of transmission). @@ -8568,7 +8573,7 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); if (nbsent) { - m_tsLastSndTime = steady_clock::now(); + m_tsLastSndTime.store(steady_clock::now()); } } } @@ -8687,7 +8692,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) case UMSG_CGWARNING: // 100 - Delay Warning // One way packet delay is increasing, so decrease the sending rate - m_tdSendInterval = (m_tdSendInterval * 1125) / 1000; + m_tdSendInterval = (m_tdSendInterval.load() * 1125) / 1000; // XXX Note as interesting fact: this is only prepared for handling, // but nothing in the code is sending this message. Probably predicted // for a custom congctl. There's a predicted place to call it under @@ -8957,7 +8962,9 @@ std::pair srt::CUDT::packData(CPacket& w_packet) const steady_clock::time_point enter_time = steady_clock::now(); if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime) - m_tdSendTimeDiff += enter_time - m_tsNextSendTime; + { + m_tdSendTimeDiff = m_tdSendTimeDiff.load() + (enter_time - m_tsNextSendTime); + } string reason = "reXmit"; @@ -9083,7 +9090,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) else { m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tdSendTimeDiff = steady_clock::duration(); return std::make_pair(0, enter_time); } } @@ -9092,7 +9099,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tdSendTimeDiff = steady_clock::duration(); return std::make_pair(0, enter_time); } @@ -9166,7 +9173,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #endif // Fix keepalive - m_tsLastSndTime = enter_time; + m_tsLastSndTime.store(enter_time); considerLegacySrtHandshake(steady_clock::time_point()); @@ -9205,18 +9212,24 @@ std::pair srt::CUDT::packData(CPacket& w_packet) else { #if USE_BUSY_WAITING - m_tsNextSendTime = enter_time + m_tdSendInterval; + m_tsNextSendTime = enter_time + m_tdSendInterval.load(); #else - if (m_tdSendTimeDiff >= m_tdSendInterval) + const duration sendint = m_tdSendInterval; + const duration sendbrw = m_tdSendTimeDiff; + + if (sendbrw >= sendint) { // Send immidiately m_tsNextSendTime = enter_time; - m_tdSendTimeDiff -= m_tdSendInterval; + + // ATOMIC NOTE: this is the only thread that + // modifies this field + m_tdSendTimeDiff = sendbrw - sendint; } else { - m_tsNextSendTime = enter_time + (m_tdSendInterval - m_tdSendTimeDiff); - m_tdSendTimeDiff = m_tdSendTimeDiff.zero(); + m_tsNextSendTime = enter_time + (sendint - sendbrw); + m_tdSendTimeDiff = duration(); } #endif } @@ -9343,7 +9356,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Just heard from the peer, reset the expiration count. m_iEXPCount = 1; - m_tsLastRspTime = steady_clock::now(); + m_tsLastRspTime.store(steady_clock::now()); const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; @@ -9792,7 +9805,7 @@ int srt::CUDT::processData(CUnit* in_unit) // a given period). if (m_CongCtl->needsQuickACK(packet)) { - m_tsNextACKTime = steady_clock::now(); + m_tsNextACKTime.store(steady_clock::now()); } } @@ -10626,7 +10639,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); result = -1; // enforce fallthrough for the below condition! - hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? SRT_REJ_IPE : m_RejectReason); + hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? int(SRT_REJ_IPE) : m_RejectReason.load()); } else { @@ -10698,7 +10711,7 @@ void srt::CUDT::addLossRecord(std::vector &lr, int32_t lo, int32_t hi) int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) { int because_decision = BECAUSE_NO_REASON; - if (currtime > m_tsNextACKTime // ACK time has come + if (currtime > m_tsNextACKTime.load() // ACK time has come // OR the number of sent packets since last ACK has reached // the congctl-defined value of ACK Interval // (note that none of the builtin congctls defines ACK Interval) @@ -10710,7 +10723,7 @@ int srt::CUDT::checkACKTimer(const steady_clock::time_point &currtime) const steady_clock::duration ack_interval = m_CongCtl->ACKTimeout_us() > 0 ? microseconds_from(m_CongCtl->ACKTimeout_us()) : m_tdACKInterval; - m_tsNextACKTime = currtime + ack_interval; + m_tsNextACKTime.store(currtime + ack_interval); m_iPktCount = 0; m_iLightACKCount = 1; @@ -10762,14 +10775,14 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) if (loss_len > 0) { - if (currtime <= m_tsNextNAKTime) + if (currtime <= m_tsNextNAKTime.load()) return BECAUSE_NO_REASON; // wait for next NAK time sendCtrl(UMSG_LOSSREPORT); debug_decision = BECAUSE_NAKREPORT; } - m_tsNextNAKTime = currtime + m_tdNAKInterval; + m_tsNextNAKTime.store(currtime + m_tdNAKInterval); return debug_decision; } @@ -10805,7 +10818,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec steady_clock::time_point next_exp_time; if (m_CongCtl->RTO()) { - next_exp_time = m_tsLastRspTime + microseconds_from(m_CongCtl->RTO()); + next_exp_time = m_tsLastRspTime.load() + microseconds_from(m_CongCtl->RTO()); } else { @@ -10813,7 +10826,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec microseconds_from(m_iEXPCount * (m_iSRTT + 4 * m_iRTTVar) + COMM_SYN_INTERVAL_US); if (exp_timeout < (m_iEXPCount * m_tdMinExpInterval)) exp_timeout = m_iEXPCount * m_tdMinExpInterval; - next_exp_time = m_tsLastRspTime + exp_timeout; + next_exp_time = m_tsLastRspTime.load() + exp_timeout; } if (currtime <= next_exp_time && !m_bBreakAsUnstable) @@ -10823,8 +10836,9 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec const int PEER_IDLE_TMO_US = m_config.iPeerIdleTimeout * 1000; // Haven't received any information from the peer, is it dead?! // timeout: at least 16 expirations and must be greater than 5 seconds + time_point last_rsp_time = m_tsLastRspTime.load(); if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && - (currtime - m_tsLastRspTime > microseconds_from(PEER_IDLE_TMO_US)))) + (currtime - last_rsp_time > microseconds_from(PEER_IDLE_TMO_US)))) { // // Connection is broken. @@ -10832,7 +10846,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec // Application will detect this when it calls any UDT methods next time. // HLOGC(xtlog.Debug, - log << "CONNECTION EXPIRED after " << count_milliseconds(currtime - m_tsLastRspTime) << "ms"); + log << "CONNECTION EXPIRED after " << count_milliseconds(currtime - last_rsp_time) << "ms"); m_bClosing = true; m_bBroken = true; m_iBrokenCounter = 30; @@ -10848,7 +10862,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec HLOGC(xtlog.Debug, log << "EXP TIMER: count=" << m_iEXPCount << "/" << (+COMM_RESPONSE_MAX_EXP) << " elapsed=" - << (count_microseconds(currtime - m_tsLastRspTime)) << "/" << (+PEER_IDLE_TMO_US) << "us"); + << (count_microseconds(currtime - last_rsp_time)) << "/" << (+PEER_IDLE_TMO_US) << "us"); ++m_iEXPCount; @@ -10969,7 +10983,7 @@ void srt::CUDT::checkTimers() // Check if FAST or LATE packet retransmission is required checkRexmitTimer(currtime); - if (currtime > m_tsLastSndTime + microseconds_from(COMM_KEEPALIVE_PERIOD_US)) + if (currtime > m_tsLastSndTime.load() + microseconds_from(COMM_KEEPALIVE_PERIOD_US)) { sendCtrl(UMSG_KEEPALIVE); #if ENABLE_EXPERIMENTAL_BONDING @@ -11051,7 +11065,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // explicitly, otherwise they will never be deleted. if (pending_broken) { - // XXX This somehow can cause a deadlock + // XXX This somehow can cause a deadlock, even without GlobControlLock // s_UDTUnited.close(m_parent); m_parent->setBrokenClosed(); } diff --git a/srtcore/core.h b/srtcore/core.h index eed601ea0..d190780a0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -56,7 +56,6 @@ modified by #include #include - #include "srt.h" #include "common.h" #include "list.h" @@ -173,6 +172,8 @@ class CUDT typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; + typedef srt::sync::AtomicClock atomic_time_point; + typedef srt::sync::AtomicDuration atomic_duration; private: // constructor and desctructor void construct(); @@ -310,7 +311,7 @@ class CUDT int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime; } + srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } @@ -723,30 +724,30 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - volatile bool m_bListening; // If the UDT entity is listening to connection - volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed - volatile bool m_bConnected; // Whether the connection is on or off - volatile bool m_bClosing; // If the UDT entity is closing - volatile bool m_bShutdown; // If the peer side has shutdown the connection - volatile bool m_bBroken; // If the connection has been broken - volatile bool m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. - volatile bool m_bPeerHealth; // If the peer status is normal - volatile int m_RejectReason; + srt::sync::atomic m_bListening; // If the UDT entity is listening to connection + srt::sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed + srt::sync::atomic m_bConnected; // Whether the connection is on or off + srt::sync::atomic m_bClosing; // If the UDT entity is closing + srt::sync::atomic m_bShutdown; // If the peer side has shutdown the connection + srt::sync::atomic m_bBroken; // If the connection has been broken + srt::sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. + srt::sync::atomic m_bPeerHealth; // If the peer status is normal + srt::sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - int m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + srt::sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter - int m_iBandwidth; // Estimated bandwidth, number of packets per second - int m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + srt::sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second + srt::sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds - int m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds - bool m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair + srt::sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + srt::sync::atomic m_bIsFirstRTTReceived;// True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. - int m_iDeliveryRate; // Packet arrival rate at the receiver side - int m_iByteDeliveryRate; // Byte arrival rate at the receiver side + srt::sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side + srt::sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response @@ -758,24 +759,24 @@ class CUDT CSndLossList* m_pSndLossList; // Sender loss list CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window - /*volatile*/ duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles + atomic_duration m_tdSendInterval; // Inter-packet time, in CPU clock cycles - /*volatile*/ duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time + atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time - volatile int m_iFlowWindowSize; // Flow control window size - volatile double m_dCongestionWindow; // Congestion window size + srt::sync::atomic m_iFlowWindowSize; // Flow control window size + double m_dCongestionWindow; // Congestion window size private: // Timers - /*volatile*/ time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below - /*volatile*/ time_point m_tsNextNAKTime; // Next NAK time - - /*volatile*/ duration m_tdACKInterval; // ACK interval - /*volatile*/ duration m_tdNAKInterval; // NAK interval - /*volatile*/ time_point m_tsLastRspTime; // Timestamp of last response from the peer - /*volatile*/ time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer - /*volatile*/ time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) + atomic_time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below + atomic_time_point m_tsNextNAKTime; // Next NAK time + + duration m_tdACKInterval; // ACK interval + duration m_tdNAKInterval; // NAK interval + atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer + time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer + atomic_time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) time_point m_tsLastWarningTime; // Last time that a warning message is sent - time_point m_tsLastReqTime; // last time when a connection request is sent + atomic_time_point m_tsLastReqTime; // last time when a connection request is sent time_point m_tsRcvPeerStartTime; time_point m_tsLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer) time_point m_tsLastAckTime; // Timestamp of last ACK @@ -787,8 +788,8 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending - volatile int32_t m_iSndLastFullAck; // Last full ACK received - volatile int32_t m_iSndLastAck; // Last ACK received + srt::sync::atomic m_iSndLastFullAck;// Last full ACK received + srt::sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) // and this is the sequence number that refers to the block at position [0]. Upon acknowledgement, @@ -798,9 +799,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list - volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - volatile int32_t m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + srt::sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list + srt::sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + srt::sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -862,7 +863,7 @@ class CUDT int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged int32_t m_iAckSeqNo; // Last ACK sequence number - int32_t m_iRcvCurrSeqNo; // Largest received sequence number + srt::sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) int32_t m_iPeerISN; // Initial Sequence Number of the peer side diff --git a/srtcore/group.h b/srtcore/group.h index ec5d124d3..549900370 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -435,7 +435,7 @@ class CUDTGroup bool m_bSyncOnMsgNo; SRT_GROUP_TYPE m_type; CUDTSocket* m_listener; // A "group" can only have one listener. - int m_iBusy; + srt::sync::atomic m_iBusy; CallbackHolder m_cbConnectHook; void installConnectHook(srt_connect_callback_fn* hook, void* opaq) { diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 9be4af013..b9d7ed02e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -242,15 +242,17 @@ void srt::CUnitQueue::makeUnitFree(CUnit* unit) SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag != CUnit::FREE); unit->m_iFlag = CUnit::FREE; + --m_iCount; } void srt::CUnitQueue::makeUnitGood(CUnit* unit) { + ++m_iCount; + SRT_ASSERT(unit != NULL); SRT_ASSERT(unit->m_iFlag == CUnit::FREE); unit->m_iFlag = CUnit::GOOD; - ++m_iCount; } srt::CSndUList::CSndUList() @@ -431,7 +433,7 @@ void srt::CSndUList::remove_(const CUDT* u) // remove the node from heap m_pHeap[n->m_iHeapLoc] = m_pHeap[m_iLastEntry]; m_iLastEntry--; - m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc; + m_pHeap[n->m_iHeapLoc]->m_iHeapLoc = n->m_iHeapLoc.load(); int q = n->m_iHeapLoc; int p = q * 2 + 1; diff --git a/srtcore/queue.h b/srtcore/queue.h index 56afff9b6..ee05440c8 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -137,7 +137,7 @@ class CUnitQueue CUnit* m_pAvailUnit; // recent available unit int m_iSize; // total size of the unit queue, in number of packets - int m_iCount; // total number of valid (occupied) packets in the queue + srt::sync::atomic m_iCount; // total number of valid (occupied) packets in the queue int m_iMSS; // unit buffer size int m_iIPversion; // IP version @@ -152,7 +152,7 @@ struct CSNode CUDT* m_pUDT; // Pointer to the instance of CUDT socket sync::steady_clock::time_point m_tsTimeStamp; - int m_iHeapLoc; // location on the heap, -1 means not on the heap + srt::sync::atomic m_iHeapLoc; // location on the heap, -1 means not on the heap }; class CSndUList @@ -240,7 +240,7 @@ struct CRNode CRNode* m_pPrev; // previous link CRNode* m_pNext; // next link - bool m_bOnList; // if the node is already on the list + srt::sync::atomic m_bOnList; // if the node is already on the list }; class CRcvUList @@ -465,7 +465,7 @@ class CSndQueue srt::sync::Mutex m_WindowLock; srt::sync::Condition m_WindowCond; - volatile bool m_bClosing; // closing the worker + srt::sync::atomic m_bClosing; // closing the worker #if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker uint64_t m_ullDbgPeriod; @@ -545,7 +545,7 @@ class CRcvQueue size_t m_szPayloadSize; // packet payload size - volatile bool m_bClosing; // closing the worker + srt::sync::atomic m_bClosing; // closing the worker #if ENABLE_LOGGING static int m_counter; #endif diff --git a/srtcore/sync.h b/srtcore/sync.h index f25c5ca8d..53123c682 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -18,10 +18,12 @@ #include #include #include +#include #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_STDCXX_STEADY #define SRT_SYNC_CLOCK_STR "STDCXX_STEADY" #else #include +#include "atomic.h" // Defile clock type to use #ifdef IA32 @@ -180,6 +182,11 @@ class TimePoint { } + TimePoint(const Duration& duration_since_epoch) + : m_timestamp(duration_since_epoch.count()) + { + } + ~TimePoint() {} public: // Relational operators @@ -224,6 +231,73 @@ inline Duration operator*(const int& lhs, const Duration +class AtomicDuration +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicDuration() ATR_NOEXCEPT : dur(0) {} + + duration_type load() + { + int64_t val = dur.load(); + return duration_type(val); + } + + void store(const duration_type& d) + { + dur.store(d.count()); + } + + AtomicDuration& operator=(const duration_type& s) + { + dur = s.count(); + return *this; + } + + operator duration_type() const + { + return duration_type(dur); + } +}; + +template +class AtomicClock +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicClock() ATR_NOEXCEPT : dur(0) {} + + time_point_type load() const + { + int64_t val = dur.load(); + return time_point_type(duration_type(val)); + } + + void store(const time_point_type& d) + { + dur.store(uint64_t(d.time_since_epoch().count())); + } + + AtomicClock& operator=(const time_point_type& s) + { + dur = s.time_since_epoch().count(); + return *this; + } + + operator time_point_type() const + { + return time_point_type(duration_type(dur.load())); + } +}; + + /////////////////////////////////////////////////////////////////////////////// // // Duration and timepoint conversions From 28a7006a3a35ec9331f6c2c310e353e9ba2a6368 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 1 Jun 2021 21:31:09 +0800 Subject: [PATCH 089/683] [core] use seq larger than m_RcvBaseSeqNo to update group readablity (#2026) --- srtcore/buffer.cpp | 62 +++++++++++++++++++++++++++++++--------------- srtcore/buffer.h | 11 ++++++-- srtcore/core.cpp | 11 +++++++- srtcore/group.cpp | 6 +++++ srtcore/group.h | 1 + 5 files changed, 68 insertions(+), 23 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index db3102e7a..b63439580 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1123,8 +1123,10 @@ size_t CRcvBuffer::dropData(int len) bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, - int32_t& w_curpktseq) + int32_t& w_curpktseq, + int32_t base_seq) { + HLOGC(brlog.Debug, log << "getRcvFirstMsg: base_seq=" << base_seq); w_skipseqno = SRT_SEQNO_NONE; w_passack = false; // tsbpdtime will be retrieved by the below call @@ -1137,8 +1139,8 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, /* Check the acknowledged packets */ // getRcvReadyMsg returns true if the time to play for the first message - // (returned in w_tsbpdtime) is in the past. - if (getRcvReadyMsg((w_tsbpdtime), (w_curpktseq), -1)) + // that larger than base_seq is in the past. + if (getRcvReadyMsg((w_tsbpdtime), (w_curpktseq), -1, base_seq)) { HLOGC(brlog.Debug, log << "getRcvFirstMsg: ready CONTIG packet: %" << w_curpktseq); return true; @@ -1167,9 +1169,10 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, * No acked packets ready but caller want to know next packet to wait for * Check the not yet acked packets that may be stuck by missing packet(s). */ - bool haslost = false; - w_tsbpdtime = steady_clock::time_point(); // redundant, for clarity - w_passack = true; + bool haslost = false; + steady_clock::time_point tsbpdtime = steady_clock::time_point(); + w_tsbpdtime = steady_clock::time_point(); + w_passack = true; // XXX SUSPECTED ISSUE with this algorithm: // The above call to getRcvReadyMsg() should report as to whether: @@ -1195,8 +1198,11 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // When done so, the below loop would be completely unnecessary. // Logical description of the below algorithm: - // 1. Check if the VERY FIRST PACKET is valid; if so then: - // - check if it's ready to play, return boolean value that marks it. + // 1. update w_tsbpdtime and w_curpktseq if found one packet ready to play + // - keep check the next packet if still smaller than base_seq + // 2. set w_skipseqno if found packets before w_curpktseq lost + // if no packets larger than base_seq ready to play, return the largest RTP + // else return the first one that larger than base_seq and rady to play for (int i = m_iLastAckPos, n = shift(m_iLastAckPos, m_iMaxPos); i != n; i = shiftFwd(i)) { @@ -1208,19 +1214,21 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, } else { - /* We got the 1st valid packet */ - w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); - if (w_tsbpdtime <= steady_clock::now()) + tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + if (tsbpdtime <= steady_clock::now()) { /* Packet ready to play */ + w_tsbpdtime = tsbpdtime; + w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; if (haslost) + w_skipseqno = w_curpktseq; + + if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) { - /* - * Packet stuck on non-acked side because of missing packets. - * Tell 1st valid packet seqno so caller can skip (drop) the missing packets. - */ - w_skipseqno = m_pUnit[i]->m_Packet.m_iSeqNo; - w_curpktseq = w_skipseqno; + HLOGC(brlog.Debug, + log << "getRcvFirstMsg: found ready packet %" << w_curpktseq + << " but not larger than base_seq, try next"); + continue; } HLOGC(brlog.Debug, @@ -1234,6 +1242,10 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // ... return true; } + + if (!is_zero(w_tsbpdtime)) { + return true; + } HLOGC(brlog.Debug, log << "getRcvFirstMsg: found NOT READY packet, nSKIPPED: " << ((i - m_iLastAckPos + m_iSize) % m_iSize)); @@ -1246,6 +1258,9 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, // the 'haslost' is set, which means that it continues only to find the first valid // packet after stating that the very first packet isn't valid. } + if (!is_zero(w_tsbpdtime)) { + return true; + } HLOGC(brlog.Debug, log << "getRcvFirstMsg: found NO PACKETS"); return false; } @@ -1276,7 +1291,7 @@ int32_t CRcvBuffer::getTopMsgno() const return m_pUnit[m_iStartPos]->m_Packet.getMsgSeq(); } -bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto) +bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq) { const bool havelimit = upto != -1; int end = -1, past_end = -1; @@ -1342,7 +1357,8 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& // 1. Get the TSBPD time of the unit. Stop and return false if this unit // is not yet ready to play. // 2. If it's ready to play, check also if it's decrypted. If not, skip it. - // 3. If it's ready to play and decrypted, stop and return it. + // 3. Check also if it's larger than base_seq, if not, skip it. + // 4. If it's ready to play, decrypted and larger than base, stop and return it. if (!havelimit) { w_tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); @@ -1361,6 +1377,12 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& IF_HEAVY_LOGGING(reason = "DECRYPTION FAILED"); freeunit = true; /* packet not decrypted */ } + else if (base_seq != SRT_SEQNO_NONE && CSeqNo::seqcmp(w_curpktseq, base_seq) <= 0) + { + IF_HEAVY_LOGGING(reason = "smaller than base_seq"); + w_tsbpdtime = steady_clock::time_point(); + freeunit = true; + } else { HLOGC(brlog.Debug, @@ -1415,7 +1437,7 @@ bool CRcvBuffer::getRcvReadyMsg(steady_clock::time_point& w_tsbpdtime, int32_t& if (freeunit) { - HLOGC(brlog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED"); + HLOGC(brlog.Debug, log << "getRcvReadyMsg: POS=" << i << " FREED: " << reason); /* removed skipped, dropped, undecryptable bytes from rcv buffer */ const int rmbytes = (int)m_pUnit[i]->m_Packet.getLength(); countBytes(-1, -rmbytes, true); diff --git a/srtcore/buffer.h b/srtcore/buffer.h index f69bef1d9..9bf02f216 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -430,11 +430,17 @@ class CRcvBuffer /// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) /// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by /// missing packets. + /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base if exist packet ready-to-play + /// and larger than base /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: /// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.; /// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play. - bool getRcvFirstMsg(time_point& w_tsbpdtime, bool& w_passack, int32_t& w_skipseqno, int32_t& w_curpktseq); + bool getRcvFirstMsg(time_point& w_tsbpdtime, + bool& w_passack, + int32_t& w_skipseqno, + int32_t& w_curpktseq, + int32_t base_seq = SRT_SEQNO_NONE); /// Update the ACK point of the buffer. /// @param [in] len size of data to be skip & acknowledged. @@ -473,9 +479,10 @@ class CRcvBuffer /// Parameters (of the 1st packet queue, ready to play or not): /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if /// none + /// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base /// @retval true 1st packet ready to play without discontinuity (no hole) /// @retval false tsbpdtime = 0: no packet ready to play - bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto); + bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq = SRT_SEQNO_NONE); public: /// @brief Get clock drift in microseconds. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 17611191e..7a337cf41 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5161,8 +5161,17 @@ void * srt::CUDT::tsbpd(void *param) int32_t current_pkt_seq = 0; steady_clock::time_point tsbpdtime; bool rxready = false; + int32_t rcv_base_seq = SRT_SEQNO_NONE; #if ENABLE_EXPERIMENTAL_BONDING bool shall_update_group = false; + if (gkeeper.group) + { + // Functions called below will lock m_GroupLock, which in hierarchy + // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock + // m_GroupLock inside the calls. + InvertedLock unrecv(self->m_RecvLock); + rcv_base_seq = gkeeper.group->getRcvBaseSeqNo(); + } #endif enterCS(self->m_RcvBufferLock); @@ -5174,7 +5183,7 @@ void * srt::CUDT::tsbpd(void *param) int32_t skiptoseqno = SRT_SEQNO_NONE; bool passack = true; // Get next packet to wait for even if not acked - rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq)); + rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq), rcv_base_seq); HLOGC(tslog.Debug, log << boolalpha << "NEXT PKT CHECK: rdy=" << rxready << " passack=" << passack << " skipto=%" diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 854508ef4..a0be7dd72 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2134,6 +2134,12 @@ void CUDTGroup::updateReadState(SRTSOCKET /* not sure if needed */, int32_t sequ } } +int32_t CUDTGroup::getRcvBaseSeqNo() +{ + ScopedLock lg(m_GroupLock); + return m_RcvBaseSeqNo; +} + void CUDTGroup::updateWriteState() { ScopedLock lg(m_GroupLock); diff --git a/srtcore/group.h b/srtcore/group.h index 549900370..e4b2fb6ed 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -345,6 +345,7 @@ class CUDTGroup void updateWriteState(); void updateFailedLink(); void activateUpdateEvent(bool still_have_items); + int32_t getRcvBaseSeqNo(); /// Update the in-group array of packet providers per sequence number. /// Also basing on the information already provided by possibly other sockets, From b4a5887964d1a56df57b7f45beb77c7e1fea51bd Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 2 Jun 2021 16:19:16 +0800 Subject: [PATCH 090/683] [build] Added compile_commands.json to .gitignore (#2031) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 10f985468..699d0e1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ _*/ # Ignore vcpkg submodule vcpkg/ + +# LSP +compile_commands.json From d6e8e213c0b374444bb5f0c04fda54197869b655 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 3 Jun 2021 11:15:12 +0800 Subject: [PATCH 091/683] [core] Fix build error when -DSRT_DEBUG_TRACE_DRIFT=1 --- srtcore/tsbpd_time.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 8fedf82ea..ff01f8a30 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -22,7 +22,7 @@ namespace srt #if SRT_DEBUG_TRACE_DRIFT class drift_logger { - using steady_clock = srt::sync::steady_clock; + typedef srt::sync::steady_clock steady_clock; public: drift_logger() {} @@ -33,12 +33,12 @@ class drift_logger m_fout.close(); } - void trace(unsigned ackack_timestamp, - int rtt_us, - int64_t drift_sample, - int64_t drift, - int64_t overdrift, - const std::chrono::steady_clock::time_point& tsbpd_base) + void trace(unsigned ackack_timestamp, + int rtt_us, + int64_t drift_sample, + int64_t drift, + int64_t overdrift, + const srt::sync::steady_clock::time_point& tsbpd_base) { using namespace srt::sync; ScopedLock lck(m_mtx); From acf38a72586cf50e94e6ff8c20344eb14067caa3 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Thu, 3 Jun 2021 16:04:06 +0800 Subject: [PATCH 092/683] [core] Fixed skip non-empty data (#2033) --- srtcore/buffer.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index b63439580..e2eb4f406 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1169,10 +1169,11 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, * No acked packets ready but caller want to know next packet to wait for * Check the not yet acked packets that may be stuck by missing packet(s). */ - bool haslost = false; - steady_clock::time_point tsbpdtime = steady_clock::time_point(); - w_tsbpdtime = steady_clock::time_point(); - w_passack = true; + bool haslost = false; + int last_ready_pos = -1; + steady_clock::time_point tsbpdtime = steady_clock::time_point(); + w_tsbpdtime = steady_clock::time_point(); + w_passack = true; // XXX SUSPECTED ISSUE with this algorithm: // The above call to getRcvReadyMsg() should report as to whether: @@ -1215,11 +1216,20 @@ bool CRcvBuffer::getRcvFirstMsg(steady_clock::time_point& w_tsbpdtime, else { tsbpdtime = getPktTsbPdTime(m_pUnit[i]->m_Packet.getMsgTimeStamp()); + /* Packet ready to play */ if (tsbpdtime <= steady_clock::now()) { - /* Packet ready to play */ - w_tsbpdtime = tsbpdtime; - w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; + // If the last ready-to-play packet exists, free it. + if (!is_zero(w_tsbpdtime)) { + HLOGC(brlog.Debug, + log << "getRcvFirstMsg: found next ready packet, free last %" + << w_curpktseq << " POS=" << last_ready_pos); + SRT_ASSERT(w_curpktseq != SRT_SEQNO_NONE); + freeUnitAt(last_ready_pos); + } + w_tsbpdtime = tsbpdtime; + w_curpktseq = m_pUnit[i]->m_Packet.m_iSeqNo; + last_ready_pos = i; if (haslost) w_skipseqno = w_curpktseq; From 5205c3cc0edeef78c3737f3c02eafad14fe1f122 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 4 Jun 2021 09:45:23 +0200 Subject: [PATCH 093/683] [docs] Edits of the requirements in ReadMe.md (#2035) --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c9aff2d8..e0403c4df 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,15 @@ As audio/video packets are streamed from a source to a destination device, SRT d ## Requirements -* cmake (as build system) -* Tcl 8.5 (optional for user-friendly build system) -* OpenSSL -* Pthreads (for POSIX systems it's builtin, for Windows there's a library) - -For detailed description of the build system and options, please read [SRT Build Options](docs/build/build-options.md). +* C++03 (or above) compliant compiler. +* CMake 2.8.12 or above (as build system). +* OpenSSL 1.1 (to enable encryption, or build with `-DENABLE_ENCRYPTION=OFF`). +* Multithreading is provided by either of the following: + * C++11: standard library (`std` by `-DENABLE_STDCXX_SYNC=ON` CMake option); + * C++03: Pthreads (for POSIX systems it's built in, for Windows there is a ported library). +* Tcl 8.5 (optional, used by `./configure` script or use CMake directly). + +For a detailed description of the build system and options, please refer to [SRT Build Options](docs/build/build-options.md). ### Build on Linux From e761745eaf8c2d1a371d1a3043f0de8c449e4f95 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 7 Jun 2021 15:39:34 +0200 Subject: [PATCH 094/683] [tests] Added fixes for FEC test occasional failure (#2037) --- test/test_fec_rebuilding.cpp | 56 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 8ea7ea26b..17f7cfbc9 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -297,7 +297,7 @@ TEST(TestFEC, Connection) ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1); ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1); - + srt_listen(l, 1); auto connect_res = std::async(std::launch::async, [&s, &sa]() { @@ -305,19 +305,21 @@ TEST(TestFEC, Connection) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + // Given 2s timeout for accepting as it has occasionally happened with Travis + // that 1s might not be enough. + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; - srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); - srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size); + EXPECT_NE(srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size), -1); + EXPECT_NE(srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size), -1); string caller_config = result_config1; string accept_config = result_config2; @@ -358,15 +360,15 @@ TEST(TestFEC, ConnectionReorder) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -411,15 +413,15 @@ TEST(TestFEC, ConnectionFull1) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -463,15 +465,15 @@ TEST(TestFEC, ConnectionFull2) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -516,15 +518,15 @@ TEST(TestFEC, ConnectionMess) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); @@ -567,15 +569,15 @@ TEST(TestFEC, ConnectionForced) }); SRTSOCKET la[] = { l }; - SRTSOCKET a = srt_accept_bond(la, 1, 1000); - EXPECT_NE(a, SRT_ERROR); + SRTSOCKET a = srt_accept_bond(la, 1, 2000); + ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config - char result_config1[200]; + char result_config1[200] = ""; int result_config1_size = 200; - char result_config2[200]; + char result_config2[200] = ""; int result_config2_size = 200; srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size); From e63b3580cbca5e7b73e172b371cb822477b84d09 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 11 Jun 2021 19:30:43 +0200 Subject: [PATCH 095/683] [apps] Output json stats values as numbers --- apps/apputil.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0333c768f..bf808a95d 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -484,7 +484,6 @@ class SrtStatsJson : public SrtStatsWriter string WriteStats(int sid, const CBytePerfMon& mon) override { std::ostringstream output; - static const string qt = R"(")"; string pretty_cr, pretty_tab; if (Option("pretty")) @@ -540,9 +539,7 @@ class SrtStatsJson : public SrtStatsWriter // Print the current field output << quotekey(i->name); - output << qt; i->PrintValue(output, mon); - output << qt; } // Close the previous subcategory From 16eca6b404a697c705ab7d37af9ea852cd77936e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 16 Jun 2021 18:59:13 +0200 Subject: [PATCH 096/683] [docs] Wrong error code for srt_accept_bond when timed out (#2040) --- docs/API/API-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 5a1fffd70..d59ce481e 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -646,7 +646,7 @@ calling this function. | [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `listeners` or `nlisteners` < 1 | | [`SRT_EINVSOCK`](#srt_einvsock) | Any socket in `listeners` designates no valid socket ID. Can also mean *Internal Error* when
an error occurred while creating an accepted socket (:warning:   **BUG?**) | | [`SRT_ENOLISTEN`](#srt_enolisten) | Any socket in `listeners` is not set up as a listener ([`srt_listen`](#srt_listen) not called, or the listener socket
has already been closed) | -| [`SRT_EASYNCRCV`](#srt_easyncrcv) | No connection reported on any listener socket as the timeout has been reached. This error is only
reported when `msTimeOut` is not -1 | +| [`SRT_ETIMEOUT`](#srt_etimeout) | No connection reported on any listener socket as the timeout has been reached. This error is only
reported when `msTimeOut` is not -1 | | | | From 8c4f288a5ef2b737e83bd77940dc9794c0e82a53 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 17 Jun 2021 15:56:30 +0200 Subject: [PATCH 097/683] [apps] Refactored app support components to make them more reusable (#2042) * Extracted componentable parts from srt-test-relay * Fixed: VerbLock is in use by testactivemedia --- apps/apputil.hpp | 15 ++ apps/logsupport.cpp | 31 +++ apps/logsupport.hpp | 2 + apps/verbose.hpp | 18 ++ testing/srt-test-live.cpp | 31 --- testing/srt-test-relay.cpp | 326 ++--------------------------- testing/srt-test-relay.maf | 1 + testing/testactivemedia.cpp | 120 +++++++++++ testing/testactivemedia.hpp | 188 +++++++++++++++++ testing/testmedia.cpp | 395 ++++++++++++++++-------------------- testing/testmedia.hpp | 53 ++++- testing/testmediabase.hpp | 4 +- 12 files changed, 630 insertions(+), 554 deletions(-) create mode 100644 testing/testactivemedia.cpp create mode 100644 testing/testactivemedia.hpp diff --git a/apps/apputil.hpp b/apps/apputil.hpp index f7bf83df3..4f2b84bf7 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -90,6 +90,21 @@ const int SysAGAIN = EAGAIN; sockaddr_any CreateAddr(const std::string& name, unsigned short port = 0, int pref_family = AF_UNSPEC); std::string Join(const std::vector& in, std::string sep); +template +struct OnReturnSetter +{ + VarType& var; + ValType value; + + OnReturnSetter(VarType& target, ValType v): var(target), value(v) {} + ~OnReturnSetter() { var = value; } +}; + +template +OnReturnSetter OnReturnSet(VarType& target, ValType v) +{ return OnReturnSetter(target, v); } + +// ---- OPTIONS MODULE inline bool CheckTrue(const std::vector& in) { diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index 2acbf64cc..fbd70c47e 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -171,4 +171,35 @@ set SrtParseLogFA(string fa, set* punknown) return fas; } +void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) +{ + std::ostringstream son, soff; + + for (auto& s: speclist) + { + string name; + bool on = true; + if (s[0] == '+') + name = s.substr(1); + else if (s[0] == '~') + { + name = s.substr(1); + on = false; + } + else + name = s; + + if (on) + son << "," << name; + else + soff << "," << name; + } + + const string& sons = son.str(); + const string& soffs = soff.str(); + + w_on = sons.empty() ? string() : sons.substr(1); + w_off = soffs.empty() ? string() : soffs.substr(1); +} + diff --git a/apps/logsupport.hpp b/apps/logsupport.hpp index 63e732560..79115d726 100644 --- a/apps/logsupport.hpp +++ b/apps/logsupport.hpp @@ -13,11 +13,13 @@ #include #include +#include #include "../srtcore/srt.h" #include "../srtcore/logging_api.h" srt_logging::LogLevel::type SrtParseLogLevel(std::string level); std::set SrtParseLogFA(std::string fa, std::set* punknown = nullptr); +void ParseLogFASpec(const std::vector& speclist, std::string& w_on, std::string& w_off); const std::map SrtLogFAList(); SRT_API extern std::map srt_level_names; diff --git a/apps/verbose.hpp b/apps/verbose.hpp index ec0276c12..10591888b 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -74,11 +74,29 @@ class ErrLog: public Log } }; +// terminal +inline void Print(Log& ) {} + +template +inline void Print(Log& out, Arg1&& arg1, Args&&... args) +{ + out << arg1; + Print(out, args...); +} + } inline Verbose::Log Verb() { return Verbose::Log(); } inline Verbose::ErrLog Verror() { return Verbose::ErrLog(); } +template +inline void Verb(Args&&... args) +{ + Verbose::Log log; + Verbose::Print(log, args...); +} + + // Manipulator tags static const Verbose::LogNoEol VerbNoEOL; #if SRT_ENABLE_VERBOSE_LOCK diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 5977b52b7..5bac09d78 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -378,37 +378,6 @@ extern "C" int SrtRejectByCodeHook(void* op, SRTSOCKET acpsock, int , const sock return -1; } -void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) -{ - std::ostringstream son, soff; - - for (auto& s: speclist) - { - string name; - bool on = true; - if (s[0] == '+') - name = s.substr(1); - else if (s[0] == '~') - { - name = s.substr(1); - on = false; - } - else - name = s; - - if (on) - son << "," << name; - else - soff << "," << name; - } - - const string& sons = son.str(); - const string& soffs = soff.str(); - - w_on = sons.empty() ? string() : sons.substr(1); - w_off = soffs.empty() ? string() : soffs.substr(1); -} - int main( int argc, char** argv ) { // This is mainly required on Windows to initialize the network system, diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 141bb8e83..1214125b7 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -15,6 +15,7 @@ written by #include "platform_sys.h" +#include #include #include #include @@ -32,6 +33,8 @@ written by #include #include +#include "testactivemedia.hpp" + #include "apputil.hpp" #include "uriparser.hpp" #include "logsupport.hpp" @@ -41,13 +44,15 @@ written by #include "testmedia.hpp" #include "threadname.h" + + + bool Upload(UriParser& srt, UriParser& file); bool Download(UriParser& srt, UriParser& file); srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-relay"); -volatile bool g_program_interrupted = false; -volatile bool g_program_established = false; +std::atomic g_program_established {false}; SrtModel* g_pending_model = nullptr; @@ -56,7 +61,7 @@ thread::id g_root_thread = std::this_thread::get_id(); static void OnINT_SetInterrupted(int) { Verb() << VerbLock << "SIGINT: Setting interrupt state."; - ::g_program_interrupted = true; + ::transmit_int_state = true; // Just for a case, forcefully close all active SRT sockets. SrtModel* pm = ::g_pending_model; @@ -83,168 +88,10 @@ static void OnINT_SetInterrupted(int) using namespace std; -template -struct OnReturnSetter -{ - VarType& var; - ValType value; - - OnReturnSetter(VarType& target, ValType v): var(target), value(v) {} - ~OnReturnSetter() { var = value; } -}; - -template -OnReturnSetter OnReturnSet(VarType& target, ValType v) -{ return OnReturnSetter(target, v); } - -template -struct Medium -{ - class SrtMainLoop* master = nullptr; - MediumDir* med = nullptr; - unique_ptr pinned_med; - list buffer; - mutex buffer_lock; - thread thr; - condition_variable ready; - volatile bool running = false; - std::exception_ptr xp; // To catch exception thrown by a thread - - virtual void Runner() = 0; - - void RunnerBase() - { - try - { - Runner(); - } - catch (...) - { - xp = std::current_exception(); - } - - //Verb() << "Medium: " << this << ": thread exit"; - unique_lock g(buffer_lock); - running = false; - ready.notify_all(); - //Verb() << VerbLock << "Medium: EXIT NOTIFIED"; - } - - void run() - { - running = true; - std::ostringstream tns; - tns << typeid(*this).name() << ":" << this; - ThreadName tn(tns.str().c_str()); - thr = thread( [this] { RunnerBase(); } ); - } - - void quit() - { - if (!med) - return; - - applog.Debug() << "Medium(" << typeid(*med).name() << ") quit. Buffer contains " << buffer.size() << " blocks"; - - string name; - if (Verbose::on) - name = typeid(*med).name(); - - med->Close(); - if (thr.joinable()) - { - applog.Debug() << "Medium::quit: Joining medium thread (" << name << ") ..."; - thr.join(); - applog.Debug() << "... done"; - } - - if (xp) - { - try { - std::rethrow_exception(xp); - } catch (TransmissionError& e) { - if (Verbose::on) - Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); - else - cerr << "Transmission Error: " << e.what() << endl; - } catch (...) { - if (Verbose::on) - Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; - else - cerr << "UNKNOWN EXCEPTION on medium\n"; - } - } - - // Prevent further quits from running - med = nullptr; - } - - void Setup(SrtMainLoop* mst, MediumDir* t) - { - med = t; - master = mst; - // Leave pinned_med as 0 - } - - void Setup(SrtMainLoop* mst, unique_ptr&& medbase) - { - pinned_med = move(medbase); - med = pinned_med.get(); - master = mst; - } - - virtual ~Medium() - { - //Verb() << "Medium: " << this << " DESTROYED. Threads quit."; - quit(); - } -}; - size_t g_chunksize = 0; size_t g_default_live_chunksize = 1316; size_t g_default_file_chunksize = 1456; -struct SourceMedium: Medium -{ - // Source Runner: read payloads and put on the buffer - void Runner() override; - - // External user: call this to get the buffer. - MediaPacket Extract(); -}; - -struct TargetMedium: Medium -{ - void Runner() override; - - bool Schedule(const MediaPacket& data) - { - lock_guard lg(buffer_lock); - if (!running || ::g_program_interrupted) - return false; - - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << data.payload.size() << "] CLIENT -> BUFFER"; - buffer.push_back(data); - ready.notify_one(); - return true; - } - - void Interrupt() - { - lock_guard lg(buffer_lock); - running = false; - ready.notify_one(); - } - - ~TargetMedium() - { - //Verb() << "TargetMedium: DESTROYING"; - Interrupt(); - // ~Medium will do quit() additionally, which joins the thread - } - -}; - class SrtMainLoop { UriParser m_srtspec; @@ -275,140 +122,6 @@ class SrtMainLoop } }; - -void SourceMedium::Runner() -{ - ThreadName::set("SourceRN"); - if (!master) - { - cerr << "IPE: incorrect setup, master empty\n"; - return; - } - - /* Don't stop me now... - struct OnReturn - { - SrtMainLoop* m; - OnReturn(SrtMainLoop* mst): m(mst) {} - ~OnReturn() - { - m->MakeStop(); - } - } on_return(master); - */ - - Verb() << VerbLock << "Starting SourceMedium: " << this; - for (;;) - { - auto input = med->Read(g_chunksize); - if (input.payload.empty() && med->End()) - { - Verb() << VerbLock << "Exiting SourceMedium: " << this; - return; - } - applog.Debug() << "SourceMedium(" << typeid(*med).name() << "): [" << input.payload.size() << "] MEDIUM -> BUFFER. signal(" << &ready << ")"; - - lock_guard g(buffer_lock); - buffer.push_back(input); - ready.notify_one(); - } -} - -MediaPacket SourceMedium::Extract() -{ - if (!master) - return {}; - - unique_lock g(buffer_lock); - for (;;) - { - if (!buffer.empty()) - { - MediaPacket top; - swap(top, *buffer.begin()); - buffer.pop_front(); - applog.Debug() << "SourceMedium(" << typeid(*med).name() << "): [" << top.payload.size() << "] BUFFER -> CLIENT"; - return top; - } - else - { - // Don't worry about the media status as long as you have somthing in the buffer. - // Purge the buffer first, then worry about the other things. - if (!running || ::g_program_interrupted) - { - applog.Debug() << "Extract(" << typeid(*med).name() << "): INTERRUPTED READING (" - << (!running ? "local" : (!master->IsRunning() ? "master" : "unknown")) << ")"; - //Verb() << "SourceMedium " << this << " not running"; - return {}; - } - - } - - // Block until ready - applog.Debug() << "Extract(" << typeid(*med).name() << "): " << this << " wait(" << &ready << ") -->"; - ready.wait_for(g, chrono::seconds(1), [this] { return running && master->IsRunning() && !buffer.empty(); }); - applog.Debug() << "Extract(" << typeid(*med).name() << "): " << this << " <-- notified (running:" - << boolalpha << running << " master:" << master->IsRunning() << " buffer:" << buffer.size() << ")"; - } -} - -void TargetMedium::Runner() -{ - ThreadName::set("TargetRN"); - auto on_return_set = OnReturnSet(running, false); - Verb() << VerbLock << "Starting TargetMedium: " << this; - for (;;) - { - MediaPacket val; - { - unique_lock lg(buffer_lock); - if (buffer.empty()) - { - if (!running) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): buffer empty, medium stopped, exiting."; - return; - } - - bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER update (timeout:" - << boolalpha << gotsomething << " running: " << running << ")"; - if (::g_program_interrupted || !running || !med || med->Broken()) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): buffer empty, medium " - << (!::g_program_interrupted ? - (running ? - (med ? - (med->Broken() ? "broken" : "UNKNOWN") - : "deleted") - : "stopped") - : "killed"); - return; - } - if (!gotsomething) // exit on timeout - continue; - } - swap(val, *buffer.begin()); - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER extraction"; - - buffer.pop_front(); - } - - // Check before writing - if (med->Broken()) - { - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER -> DISCARDED (medium broken)"; - running = false; - return; - } - - applog.Debug() << "TargetMedium(" << typeid(*med).name() << "): [" << val.payload.size() << "] BUFFER -> MEDIUM"; - // You get the data to send, send them. - med->Write(val); - } -} - - int main( int argc, char** argv ) { OptionName @@ -605,7 +318,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin { Verb() << "Setting up output: " << spec; unique_ptr m { new TargetMedium }; - m->Setup(this, Target::Create(spec)); + m->Setup(Target::Create(spec)); m_output_media.push_back(move(m)); } @@ -631,14 +344,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "... Established. configuring other pipes:"; // Once it's ready, use it to initialize the medium. - - m_srt_relay.reset(new SrtRelay); - m_srt_relay->StealFrom(m); - - m_srt_source.Setup(this, m_srt_relay.get()); - bool file_mode = (transtype == "file"); - if (g_chunksize == 0) { if (file_mode) @@ -649,6 +355,11 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "DEFAULT CHUNKSIZE used: " << g_chunksize; } + m_srt_relay.reset(new SrtRelay); + m_srt_relay->StealFrom(m); + + m_srt_source.Setup(m_srt_relay.get(), g_chunksize); + // Now check the input medium if (input_echoback) { @@ -656,7 +367,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin // Add SRT medium to output targets, and keep input medium empty. unique_ptr m { new TargetMedium }; - m->Setup(this, m_srt_relay.get()); + m->Setup(m_srt_relay.get()); m_output_media.push_back(move(m)); } else @@ -665,7 +376,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin // to the output list, as this will be fed directly // by the data from this input medium in a spearate engine. Verb() << "Setting up input: " << input_spec; - m_input_medium.Setup(this, Source::Create(input_spec)); + m_input_medium.Setup(Source::Create(input_spec), g_chunksize); if (!file_mode) { @@ -796,6 +507,11 @@ void SrtMainLoop::run() } Verb() << "MEDIA LOOP EXIT"; + for (auto& m : m_output_media) + { + m->quit(); + } + m_input_medium.quit(); m_srt_source.quit(); if (m_input_xp) diff --git a/testing/srt-test-relay.maf b/testing/srt-test-relay.maf index b1b22afb2..33cba8173 100644 --- a/testing/srt-test-relay.maf +++ b/testing/srt-test-relay.maf @@ -3,6 +3,7 @@ SOURCES srt-test-relay.cpp testmedia.cpp +testactivemedia.cpp ../apps/apputil.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp new file mode 100644 index 000000000..765d22ef6 --- /dev/null +++ b/testing/testactivemedia.cpp @@ -0,0 +1,120 @@ + +#include "testactivemedia.hpp" + +void SourceMedium::Runner() +{ + ThreadName::set("SourceRN"); + + Verb() << VerbLock << "Starting SourceMedium: " << this; + for (;;) + { + auto input = med->Read(chunksize_); + if (input.payload.empty() && med->End()) + { + Verb() << VerbLock << "Exiting SourceMedium: " << this; + return; + } + LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", input.payload.size(), "] MEDIUM -> BUFFER. signal(", &ready, ")"); + + lock_guard g(buffer_lock); + buffer.push_back(input); + ready.notify_one(); + } +} + +MediaPacket SourceMedium::Extract() +{ + unique_lock g(buffer_lock); + for (;;) + { + if (::transmit_int_state) + running = false; + + if (!buffer.empty()) + { + MediaPacket top; + swap(top, *buffer.begin()); + buffer.pop_front(); + LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", top.payload.size(), "] BUFFER -> CLIENT"); + return top; + } + else + { + // Don't worry about the media status as long as you have somthing in the buffer. + // Purge the buffer first, then worry about the other things. + if (!running) + { + //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): INTERRUPTED READING"); + //Verb() << "SourceMedium " << this << " not running"; + return {}; + } + + } + + // Block until ready + //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " wait(", &ready, ") -->"); + + ready.wait_for(g, chrono::seconds(1), [this] { return running && !buffer.empty(); }); + + // LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " <-- notified (running:" + // << boolalpha << running << " buffer:" << buffer.size() << ")"); + } +} + +void TargetMedium::Runner() +{ + ThreadName::set("TargetRN"); + auto on_return_set = OnReturnSet(running, false); + Verb() << VerbLock << "Starting TargetMedium: " << this; + for (;;) + { + MediaPacket val; + { + unique_lock lg(buffer_lock); + if (buffer.empty()) + { + if (!running) + { + //LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium stopped, exiting."); + return; + } + + bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:", + boolalpha, gotsomething, " running: ", running, ")"); + if (::transmit_int_state || !running || !med || med->Broken()) + { + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ", + (!::transmit_int_state ? + (running ? + (med ? + (med->Broken() ? "broken" : "UNKNOWN") + : "deleted") + : "stopped") + : "killed")); + return; + } + if (!gotsomething) // exit on timeout + continue; + } + swap(val, *buffer.begin()); + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER extraction"); + + buffer.pop_front(); + } + + // Check before writing + if (med->Broken()) + { + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> DISCARDED (medium broken)"); + running = false; + return; + } + + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> MEDIUM"); + // You get the data to send, send them. + med->Write(val); + } +} + + diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp new file mode 100644 index 000000000..b94ff9aa3 --- /dev/null +++ b/testing/testactivemedia.hpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "testmedia.hpp" +#include "logsupport.hpp" + +#define SRT_ENABLE_VERBOSE_LOCK 1 +#include "verbose.hpp" + +#include "logging.h" +#include "threadname.h" + +extern srt_logging::Logger applog; + +template +struct Medium +{ + MediumDir* med = nullptr; + std::unique_ptr pinned_med; + std::list buffer; + std::mutex buffer_lock; + std::thread thr; + std::condition_variable ready; + std::atomic running {false}; + std::exception_ptr xp; // To catch exception thrown by a thread + + virtual void Runner() = 0; + + void RunnerBase() + { + try + { + running = true; + Runner(); + } + catch (...) + { + xp = std::current_exception(); + } + + //Verb() << "Medium: " << this << ": thread exit"; + std::unique_lock g(buffer_lock); + running = false; + ready.notify_all(); + //Verb() << VerbLock << "Medium: EXIT NOTIFIED"; + } + + void run() + { + running = true; + std::ostringstream tns; + tns << typeid(*this).name() << ":" << this; + ThreadName tn(tns.str().c_str()); + thr = thread( [this] { RunnerBase(); } ); + } + + void quit() + { + if (!med) + return; + + LOGP(applog.Debug, "Medium(", typeid(*med).name(), ") quit. Buffer contains ", buffer.size(), " blocks"); + + std::string name; + if (Verbose::on) + name = typeid(*med).name(); + + med->Close(); + if (thr.joinable()) + { + LOGP(applog.Debug, "Medium::quit: Joining medium thread (", name, ") ..."); + thr.join(); + LOGP(applog.Debug, "... done"); + } + + if (xp) + { + try { + std::rethrow_exception(xp); + } catch (TransmissionError& e) { + if (Verbose::on) + Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); + else + cerr << "Transmission Error: " << e.what() << endl; + } catch (...) { + if (Verbose::on) + Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; + else + cerr << "UNKNOWN EXCEPTION on medium\n"; + } + } + + // Prevent further quits from running + med = nullptr; + } + + void Setup(MediumDir* t) + { + med = t; + // Leave pinned_med as 0 + } + + void Setup(std::unique_ptr&& medbase) + { + pinned_med = std::move(medbase); + med = pinned_med.get(); + } + + virtual ~Medium() + { + //Verb() << "Medium: " << this << " DESTROYED. Threads quit."; + quit(); + } + + virtual void Start() { run(); } + virtual void Stop() { quit(); } +}; + +struct SourceMedium: Medium +{ + size_t chunksize_ = 0; + typedef Medium Base; + + // Source Runner: read payloads and put on the buffer + void Runner() override; + + // External user: call this to get the buffer. + MediaPacket Extract(); + + template + void Setup(Arg&& medium, size_t chunksize) + { + chunksize_ = chunksize; + return Base::Setup(std::move(medium)); + } +}; + +struct TargetMedium: Medium +{ + void Runner() override; + + bool Schedule(const MediaPacket& data) + { + LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); + lock_guard lg(buffer_lock); + LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); + if (!running || ::transmit_int_state) + { + LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); + return false; + } + + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): Schedule: [", data.payload.size(), "] CLIENT -> BUFFER"); + buffer.push_back(data); + ready.notify_one(); + return true; + } + + void Clear() + { + lock_guard lg(buffer_lock); + buffer.clear(); + } + + void Interrupt() + { + lock_guard lg(buffer_lock); + running = false; + ready.notify_one(); + } + + ~TargetMedium() + { + //Verb() << "TargetMedium: DESTROYING"; + Interrupt(); + // ~Medium will do quit() additionally, which joins the thread + } +}; + + diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 501e5f8d7..f986aa5dc 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #if !defined(_WIN32) #include @@ -48,8 +49,8 @@ using srt_logging::SockStatusStr; using srt_logging::MemberStatusStr; #endif -volatile bool transmit_throw_on_interrupt = false; -volatile bool transmit_int_state = false; +std::atomic transmit_throw_on_interrupt {false}; +std::atomic transmit_int_state {false}; int transmit_bw_report = 0; unsigned transmit_stats_report = 0; size_t transmit_chunk_size = SRT_LIVE_DEF_PLSIZE; @@ -2715,276 +2716,238 @@ static inline bool IsMulticast(in_addr adr) return c >= 224 && c <= 239; } - -class UdpCommon +void UdpCommon::Setup(string host, int port, map attr) { -protected: - int m_sock = -1; - sockaddr_any sadr; - string adapter; - map m_options; + m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (m_sock == -1) + Error(SysError(), "UdpCommon::Setup: socket"); - void Setup(string host, int port, map attr) - { - m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (m_sock == -1) - Error(SysError(), "UdpCommon::Setup: socket"); + int yes = 1; + ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes); - int yes = 1; - ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes); + sadr = CreateAddr(host, port); - sadr = CreateAddr(host, port); + bool is_multicast = false; + if (sadr.family() == AF_INET) + { + if (attr.count("multicast")) + { + if (!IsMulticast(sadr.sin.sin_addr)) + { + throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address"); + } + is_multicast = true; + } + else if (IsMulticast(sadr.sin.sin_addr)) + { + is_multicast = true; + } - bool is_multicast = false; - if (sadr.family() == AF_INET) + if (is_multicast) { - if (attr.count("multicast")) + ip_mreq_source mreq_ssm; + ip_mreq mreq; + sockaddr_any maddr; + int opt_name; + void* mreq_arg_ptr; + socklen_t mreq_arg_size; + + adapter = attr.count("adapter") ? attr.at("adapter") : string(); + if (adapter == "") { - if (!IsMulticast(sadr.sin.sin_addr)) - { - throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address"); - } - is_multicast = true; + Verb() << "Multicast: home address: INADDR_ANY:" << port; + maddr.sin.sin_family = AF_INET; + maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY); + maddr.sin.sin_port = htons(port); // necessary for temporary use } - else if (IsMulticast(sadr.sin.sin_addr)) + else { - is_multicast = true; + Verb() << "Multicast: home address: " << adapter << ":" << port; + maddr = CreateAddr(adapter, port); } - if (is_multicast) + if (attr.count("source")) { - ip_mreq_source mreq_ssm; - ip_mreq mreq; - sockaddr_any maddr; - int opt_name; - void* mreq_arg_ptr; - socklen_t mreq_arg_size; - - adapter = attr.count("adapter") ? attr.at("adapter") : string(); - if (adapter == "") - { - Verb() << "Multicast: home address: INADDR_ANY:" << port; - maddr.sin.sin_family = AF_INET; - maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY); - maddr.sin.sin_port = htons(port); // necessary for temporary use - } - else - { - Verb() << "Multicast: home address: " << adapter << ":" << port; - maddr = CreateAddr(adapter, port); - } - - if (attr.count("source")) - { - /* this is an ssm. we need to use the right struct and opt */ - opt_name = IP_ADD_SOURCE_MEMBERSHIP; - mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; - mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; - inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr); - mreq_arg_size = sizeof(mreq_ssm); - mreq_arg_ptr = &mreq_ssm; - } - else - { - opt_name = IP_ADD_MEMBERSHIP; - mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; - mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; - mreq_arg_size = sizeof(mreq); - mreq_arg_ptr = &mreq; - } + /* this is an ssm. we need to use the right struct and opt */ + opt_name = IP_ADD_SOURCE_MEMBERSHIP; + mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; + mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; + inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr); + mreq_arg_size = sizeof(mreq_ssm); + mreq_arg_ptr = &mreq_ssm; + } + else + { + opt_name = IP_ADD_MEMBERSHIP; + mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr; + mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr; + mreq_arg_size = sizeof(mreq); + mreq_arg_ptr = &mreq; + } #ifdef _WIN32 - const char* mreq_arg = (const char*)mreq_arg_ptr; - const auto status_error = SOCKET_ERROR; + const char* mreq_arg = (const char*)mreq_arg_ptr; + const auto status_error = SOCKET_ERROR; #else - const void* mreq_arg = mreq_arg_ptr; - const auto status_error = -1; + const void* mreq_arg = mreq_arg_ptr; + const auto status_error = -1; #endif #if defined(_WIN32) || defined(__CYGWIN__) - // On Windows it somehow doesn't work when bind() - // is called with multicast address. Write the address - // that designates the network device here. - // Also, sets port sharing when working with multicast - sadr = maddr; - int reuse = 1; - int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)); - if (shareAddrRes == status_error) - { - throw runtime_error("marking socket for shared use failed"); - } - Verb() << "Multicast(Windows): will bind to home address"; + // On Windows it somehow doesn't work when bind() + // is called with multicast address. Write the address + // that designates the network device here. + // Also, sets port sharing when working with multicast + sadr = maddr; + int reuse = 1; + int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)); + if (shareAddrRes == status_error) + { + throw runtime_error("marking socket for shared use failed"); + } + Verb() << "Multicast(Windows): will bind to home address"; #else - Verb() << "Multicast(POSIX): will bind to IGMP address: " << host; + Verb() << "Multicast(POSIX): will bind to IGMP address: " << host; #endif - int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size); - - if (res == status_error) - { - Error(errno, "adding to multicast membership failed"); - } + int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size); - attr.erase("multicast"); - attr.erase("adapter"); + if (res == status_error) + { + Error(errno, "adding to multicast membership failed"); } + + attr.erase("multicast"); + attr.erase("adapter"); } + } - // The "ttl" options is handled separately, it maps to both IP_TTL - // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast. - if (attr.count("ttl")) - { - int ttl = stoi(attr.at("ttl")); - int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl); - if (res == -1) - Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl; - res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl); - if (res == -1) - Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl; + // The "ttl" options is handled separately, it maps to both IP_TTL + // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast. + if (attr.count("ttl")) + { + int ttl = stoi(attr.at("ttl")); + int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl); + if (res == -1) + Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl; + res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl); + if (res == -1) + Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl; - attr.erase("ttl"); - } + attr.erase("ttl"); + } - m_options = attr; + m_options = attr; - for (auto o: udp_options) + for (auto o: udp_options) + { + // Ignore "binding" - for UDP there are no post options. + if (m_options.count(o.name)) { - // Ignore "binding" - for UDP there are no post options. - if (m_options.count(o.name)) - { - string value = m_options.at(o.name); - bool ok = o.apply(m_sock, value); - if (!ok) - Verb() << "WARNING: failed to set '" << o.name << "' to " << value; - } + string value = m_options.at(o.name); + bool ok = o.apply(m_sock, value); + if (!ok) + Verb() << "WARNING: failed to set '" << o.name << "' to " << value; } } +} - void Error(int err, string src) - { - char buf[512]; - string message = SysStrError(err, buf, 512u); +void UdpCommon::Error(int err, string src) +{ + char buf[512]; + string message = SysStrError(err, buf, 512u); - if (Verbose::on) - Verb() << "FAILURE\n" << src << ": [" << err << "] " << message; - else - cerr << "\nERROR #" << err << ": " << message << endl; + if (Verbose::on) + Verb() << "FAILURE\n" << src << ": [" << err << "] " << message; + else + cerr << "\nERROR #" << err << ": " << message << endl; - throw TransmissionError("error: " + src + ": " + message); - } + throw TransmissionError("error: " + src + ": " + message); +} - ~UdpCommon() - { +UdpCommon::~UdpCommon() +{ #ifdef _WIN32 - if (m_sock != -1) - { - shutdown(m_sock, SD_BOTH); - closesocket(m_sock); - m_sock = -1; - } + if (m_sock != -1) + { + shutdown(m_sock, SD_BOTH); + closesocket(m_sock); + m_sock = -1; + } #else - close(m_sock); + close(m_sock); #endif - } -}; - +} -class UdpSource: public virtual Source, public virtual UdpCommon +UdpSource::UdpSource(string host, int port, const map& attr) { - bool eof = true; -public: + Setup(host, port, attr); + int stat = ::bind(m_sock, sadr.get(), sadr.size()); + if (stat == -1) + Error(SysError(), "Binding address for UDP"); + eof = false; + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) + Error(SysError(), "Setting timeout for UDP"); +} - UdpSource(string host, int port, const map& attr) +MediaPacket UdpSource::Read(size_t chunk) +{ + bytevector data(chunk); + sockaddr_any sa(sadr.family()); + int64_t srctime = 0; +AGAIN: + int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen()); + int err = SysError(); + if (transmit_use_sourcetime) { - Setup(host, port, attr); - int stat = ::bind(m_sock, sadr.get(), sadr.size()); - if (stat == -1) - Error(SysError(), "Binding address for UDP"); - eof = false; - struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0) - Error(SysError(), "Setting timeout for UDP"); + srctime = srt_time_now(); } - - MediaPacket Read(size_t chunk) override + if (stat == -1) { - bytevector data(chunk); - sockaddr_any sa(sadr.family()); - int64_t srctime = 0; -AGAIN: - int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen()); - int err = SysError(); - if (transmit_use_sourcetime) - { - srctime = srt_time_now(); - } - if (stat == -1) - { - if (!::transmit_int_state && err == SysAGAIN) - goto AGAIN; + if (!::transmit_int_state && err == SysAGAIN) + goto AGAIN; - Error(SysError(), "UDP Read/recvfrom"); - } - - if (stat < 1) - { - eof = true; - return bytevector(); - } - - chunk = size_t(stat); - if (chunk < data.size()) - data.resize(chunk); + Error(SysError(), "UDP Read/recvfrom"); + } - return MediaPacket(data, srctime); + if (stat < 1) + { + eof = true; + return bytevector(); } - bool IsOpen() override { return m_sock != -1; } - bool End() override { return eof; } -}; + chunk = size_t(stat); + if (chunk < data.size()) + data.resize(chunk); + + return MediaPacket(data, srctime); +} -class UdpTarget: public virtual Target, public virtual UdpCommon +UdpTarget::UdpTarget(string host, int port, const map& attr) { -public: - UdpTarget(string host, int port, const map& attr ) + Setup(host, port, attr); + if (adapter != "") { - Setup(host, port, attr); - if (adapter != "") - { - auto maddr = CreateAddr(adapter, 0); - in_addr addr = maddr.sin.sin_addr; + auto maddr = CreateAddr(adapter, 0); + in_addr addr = maddr.sin.sin_addr; - int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast(&addr), sizeof(addr)); - if (res == -1) - { - Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter); - } + int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast(&addr), sizeof(addr)); + if (res == -1) + { + Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter); } } +} - void Write(const MediaPacket& data) override - { - int stat = sendto(m_sock, data.payload.data(), data.payload.size(), 0, (sockaddr*)&sadr, sizeof sadr); - if (stat == -1) - Error(SysError(), "UDP Write/sendto"); - } - - bool IsOpen() override { return m_sock != -1; } - bool Broken() override { return false; } -}; - -class UdpRelay: public Relay, public UdpSource, public UdpTarget +void UdpTarget::Write(const MediaPacket& data) { -public: - UdpRelay(string host, int port, const map& attr): - UdpSource(host, port, attr), - UdpTarget(host, port, attr) - { - } + int stat = sendto(m_sock, data.payload.data(), data.payload.size(), 0, (sockaddr*)&sadr, sizeof sadr); + if (stat == -1) + Error(SysError(), "UDP Write/sendto"); +} - bool IsOpen() override { return m_sock != -1; } -}; template struct Udp; template <> struct Udp { typedef UdpSource type; }; diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index b251f140b..d3447a0d0 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -15,14 +15,16 @@ #include #include #include +#include +#include "apputil.hpp" #include "testmediabase.hpp" #include // Needs access to CUDTException #include extern srt_listen_callback_fn* transmit_accept_hook_fn; extern void* transmit_accept_hook_op; -extern volatile bool transmit_int_state; +extern std::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; @@ -298,6 +300,55 @@ class SrtModel: public SrtCommon } }; +class UdpCommon +{ +protected: + int m_sock = -1; + sockaddr_any sadr; + std::string adapter; + std::map m_options; + void Setup(std::string host, int port, std::map attr); + void Error(int err, std::string src); + + ~UdpCommon(); +}; + + +class UdpSource: public virtual Source, public virtual UdpCommon +{ + bool eof = true; +public: + + UdpSource(string host, int port, const map& attr); + + MediaPacket Read(size_t chunk) override; + + bool IsOpen() override { return m_sock != -1; } + bool End() override { return eof; } +}; + +class UdpTarget: public virtual Target, public virtual UdpCommon +{ +public: + UdpTarget(string host, int port, const map& attr); + + void Write(const MediaPacket& data) override; + bool IsOpen() override { return m_sock != -1; } + bool Broken() override { return false; } +}; + +class UdpRelay: public Relay, public UdpSource, public UdpTarget +{ +public: + UdpRelay(string host, int port, const map& attr): + UdpSource(host, port, attr), + UdpTarget(host, port, attr) + { + } + + bool IsOpen() override { return m_sock != -1; } +}; + #endif diff --git a/testing/testmediabase.hpp b/testing/testmediabase.hpp index a04803e04..3eb16a4bb 100644 --- a/testing/testmediabase.hpp +++ b/testing/testmediabase.hpp @@ -17,8 +17,10 @@ #include #include +#include "uriparser.hpp" + typedef std::vector bytevector; -extern volatile bool transmit_throw_on_interrupt; +extern std::atomic transmit_throw_on_interrupt; extern int transmit_bw_report; extern unsigned transmit_stats_report; extern size_t transmit_chunk_size; From e932e8fbdda4a053023985a6f9c076cf09810e98 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 17:48:48 +0200 Subject: [PATCH 098/683] [core] Fixed getTsbPdTimeBase: carryover within 2 wrapping periods (#2043) --- srtcore/tsbpd_time.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index ff01f8a30..54a8c5df5 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -38,6 +38,7 @@ class drift_logger int64_t drift_sample, int64_t drift, int64_t overdrift, + const srt::sync::steady_clock::time_point& pkt_base, const srt::sync::steady_clock::time_point& tsbpd_base) { using namespace srt::sync; @@ -50,6 +51,9 @@ class drift_logger std::string str_tbase = srt::sync::FormatTime(tsbpd_base); str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part + std::string str_pkt_base = srt::sync::FormatTime(pkt_base); + str_pkt_base.resize(str_pkt_base.size() - 7); // remove trailing ' [STDY]' part + // m_fout << str_tnow << ","; m_fout << count_microseconds(steady_clock::now() - m_start_time) << ","; m_fout << ackack_timestamp << ","; @@ -57,6 +61,7 @@ class drift_logger m_fout << drift_sample << ","; m_fout << drift << ","; m_fout << overdrift << ","; + m_fout << str_pkt_base << ","; m_fout << str_tbase << "\n"; m_fout.flush(); } @@ -65,7 +70,7 @@ class drift_logger void print_header() { m_fout << "usElapsedStd,usAckAckTimestampStd,"; - m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,TSBPDBase\n"; + m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,tsPktBase,TSBPDBase\n"; } void create_file() @@ -121,8 +126,9 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, // A change in network delay has to be taken into account. The only way to get some estimation of it // is to estimate RTT change and assume that the change of the one way network delay is // approximated by the half of the RTT change. - const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); - const steady_clock::duration tdDrift = tsNow - getPktTsbPdBaseTime(usPktTimestamp) - tdRTTDelta; + const duration tdRTTDelta = microseconds_from((usRTTSample - m_iFirstRTT) / 2); + const time_point tsPktBaseTime = getPktTsbPdBaseTime(usPktTimestamp); + const steady_clock::duration tdDrift = tsNow - tsPktBaseTime - tdRTTDelta; const bool updated = m_DriftTracer.update(count_microseconds(tdDrift)); @@ -152,6 +158,7 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, count_microseconds(tdDrift), m_DriftTracer.drift(), m_DriftTracer.overdrift(), + tsPktBaseTime, m_tsTsbPdTimeBase); #endif return updated; @@ -168,7 +175,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra // // This function is called in the HSREQ reception handler only. m_tsTsbPdTimeBase = timebase; - m_tdTsbPdDelay = delay; + m_tdTsbPdDelay = delay; } void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase, @@ -210,8 +217,11 @@ void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase, CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const { - const uint64_t carryover_us = - (m_bTsbPdWrapCheck && timestamp_us < TSBPD_WRAP_PERIOD) ? uint64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; + // A data packet within [TSBPD_WRAP_PERIOD; 2 * TSBPD_WRAP_PERIOD] would end TSBPD wrap-aware state. + // Some incoming control packets may not update the TSBPD base (calling updateTsbPdTimeBase(..)), + // but may come before a data packet with a timestamp in this range. Therefore the whole range should be tracked. + const int64_t carryover_us = + (m_bTsbPdWrapCheck && timestamp_us <= 2 * TSBPD_WRAP_PERIOD) ? int64_t(CPacket::MAX_TIMESTAMP) + 1 : 0; return (m_tsTsbPdTimeBase + microseconds_from(carryover_us)); } @@ -243,14 +253,14 @@ void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp) return; } - // Check if timestamp is in the last 30 seconds before reaching the MAX_TIMESTAMP. + // Check if timestamp is within the TSBPD_WRAP_PERIOD before reaching the MAX_TIMESTAMP. if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD)) { // Approching wrap around point, start wrap check period (if for packet delivery head) m_bTsbPdWrapCheck = true; LOGC(tslog.Debug, - log << "tsbpd wrap period begins with ts=" << usPktTimestamp << " drift: " << m_DriftTracer.drift() - << "us."); + log << "tsbpd wrap period begins with ts=" << usPktTimestamp + << " TIME BASE: " << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us."); } } From 94322d4081b45153b6c1fe7785d6973b472de497 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 2 Jul 2021 21:06:28 +0800 Subject: [PATCH 099/683] [core] Fix unused-variable warning --- srtcore/api.cpp | 2 +- srtcore/buffer.cpp | 4 ++-- srtcore/channel.cpp | 2 +- srtcore/core.cpp | 12 ++++++------ srtcore/crypto.cpp | 7 ++++--- srtcore/epoll.cpp | 4 ++-- srtcore/fec.cpp | 2 +- srtcore/group.cpp | 6 +++--- srtcore/logging.h | 4 ++-- srtcore/sync.h | 3 ++- srtcore/utilities.h | 7 +++---- test/test_listen_callback.cpp | 2 +- testing/testmedia.cpp | 2 +- 13 files changed, 29 insertions(+), 28 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index aee13f389..d38442590 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1384,7 +1384,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // Set all options that were requested by the options set on a group // prior to connecting. - string error_reason ATR_UNUSED; + string error_reason SRT_ATR_UNUSED; try { for (size_t i = 0; i < g.m_config.size(); ++i) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e2eb4f406..bbe41b7f6 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -986,8 +986,8 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) int lastack = m_iLastAckPos; int rs = len; - int32_t trace_seq ATR_UNUSED = SRT_SEQNO_NONE; - int trace_shift ATR_UNUSED = -1; + int32_t trace_seq SRT_ATR_UNUSED = SRT_SEQNO_NONE; + int trace_shift SRT_ATR_UNUSED = -1; while ((p != lastack) && (rs > 0)) { diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 6e56e97f7..92fd6b991 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -183,7 +183,7 @@ void srt::CChannel::createSocket(int family) if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) { - const int res ATR_UNUSED = + const int res SRT_ATR_UNUSED = ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&m_mcfg.iIpV6Only, sizeof m_mcfg.iIpV6Only); #if ENABLE_LOGGING if (res == -1) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7a337cf41..15a138a3a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2385,7 +2385,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint // This function is called only when the URQ_CONCLUSION handshake has been received from the peer. bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, - uint32_t* out_data, + uint32_t* out_data SRT_ATR_UNUSED, size_t* pw_len) { // Initialize pw_len to 0 to handle the unencrypted case @@ -2440,7 +2440,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, uint32_t* p = reinterpret_cast(hspkt.m_pcData + CHandShake::m_iContentSize); size_t size = hspkt.getLength() - CHandShake::m_iContentSize; // Due to previous cond check we grant it's >0 - int hsreq_type_cmd ATR_UNUSED = SRT_CMD_NONE; + int hsreq_type_cmd SRT_ATR_UNUSED = SRT_CMD_NONE; if (IsSet(ext_flags, CHandShake::HS_EXT_HSREQ)) { @@ -2671,7 +2671,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_config.sCongestion.set("live", 4); } - bool have_group ATR_UNUSED = false; + bool have_group SRT_ATR_UNUSED = false; if (IsSet(ext_flags, CHandShake::HS_EXT_CONFIG)) { @@ -7715,7 +7715,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t first_seq ATR_UNUSED = ackDataUpTo(ack); + const int32_t first_seq SRT_ATR_UNUSED = ackDataUpTo(ack); InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8318,7 +8318,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr { steady_clock::duration udrift(0); steady_clock::time_point newtimebase; - const bool drift_updated ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), + const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), rtt, (udrift), (newtimebase)); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) @@ -10795,7 +10795,7 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) return debug_decision; } -bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason ATR_UNUSED) +bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int check_reason SRT_ATR_UNUSED) { // VERY HEAVY LOGGING #if ENABLE_HEAVY_LOGGING & 1 diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index c7c27abab..6c482a586 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -43,7 +43,7 @@ using namespace srt_logging; */ // 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE -const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000; +const int SRT_CRYPT_KM_PRE_ANNOUNCE SRT_ATR_UNUSED = 0x10000; namespace srt_logging { @@ -662,6 +662,8 @@ std::string CCryptoControl::CONID() const return os.str(); } +#ifdef SRT_ENABLE_ENCRYPTION + #if ENABLE_HEAVY_LOGGING static std::string CryptoFlags(int flg) { @@ -679,9 +681,8 @@ static std::string CryptoFlags(int flg) copy(f.begin(), f.end(), ostream_iterator(os, "|")); return os.str(); } -#endif +#endif // ENABLE_HEAVY_LOGGING -#ifdef SRT_ENABLE_ENCRYPTION bool CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto) { diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 71945bdbd..548418f88 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -713,7 +713,7 @@ int CEPoll::wait(const int eid, set* readfds, set* writefd throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0); } - const bool wait_signaled ATR_UNUSED = CGlobEvent::waitForEvent(); + const bool wait_signaled SRT_ATR_UNUSED = CGlobEvent::waitForEvent(); HLOGC(ealog.Debug, log << "CEPoll::wait: EVENT WAITING: " << (wait_signaled ? "TRIGGERED" : "CHECKPOINT")); } @@ -779,7 +779,7 @@ int CEPoll::swait(CEPollDesc& d, map& st, int64_t msTimeOut, boo st[i->fd] = i->events; IF_HEAVY_LOGGING(singles << "@" << i->fd << ":"); IF_HEAVY_LOGGING(PrintEpollEvent(singles, i->events, i->parent->edgeOnly())); - const bool edged ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i` + const bool edged SRT_ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i` IF_HEAVY_LOGGING(singles << (edged ? "<^> " : " ")); } diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 31239eed8..61adc0957 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -2029,7 +2029,7 @@ void FECFilterBuiltin::RcvCheckDismissColumn(int32_t seq, int colgx, loss_seqs_t any_dismiss = true; const int32_t newbase = rcv.colq[numberCols()].base; - int32_t newbase_row ATR_UNUSED; // For logging only, but including FATAL. + int32_t newbase_row SRT_ATR_UNUSED; // For logging only, but including FATAL. // Sanity check // If sanity check failed OR if the number of existing row // groups doesn't enclose those that need to be dismissed, diff --git a/srtcore/group.cpp b/srtcore/group.cpp index a0be7dd72..f2f99201b 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3168,7 +3168,7 @@ CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_ } // [[using locked(this->m_GroupLock)]] -bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime ATR_UNUSED, +bool CUDTGroup::sendBackup_CheckSendStatus(const steady_clock::time_point& currtime SRT_ATR_UNUSED, const int send_status, const int32_t lastseq, const int32_t pktseq, @@ -3769,7 +3769,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx // suffer linear search. int nwaiting = 0; - int nactivated ATR_UNUSED = 0; + int nactivated SRT_ATR_UNUSED = 0; int stat = -1; for (gli_t d = m_Group.begin(); d != m_Group.end(); ++d) { @@ -4217,7 +4217,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // sequences already used by the link from which packets were // copied to the backup buffer. IF_HEAVY_LOGGING(int32_t old = core.schedSeqNo()); - const bool su ATR_UNUSED = core.overrideSndSeqNo(curseq); + const bool su SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); HLOGC(gslog.Debug, log << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq << (su ? " - succeeded" : " - FAILED!")); diff --git a/srtcore/logging.h b/srtcore/logging.h index cfb239f1e..8767f2aa7 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -422,7 +422,7 @@ inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) } template -inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED) +inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING std::ostringstream serr; @@ -440,7 +440,7 @@ inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line AT #else template -inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED) +inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING std::ostringstream serr; diff --git a/srtcore/sync.h b/srtcore/sync.h index 53123c682..7da6952f7 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -51,6 +51,7 @@ #endif // ENABLE_STDCXX_SYNC +#include "srt.h" #include "utilities.h" class CUDTException; // defined in common.h @@ -596,7 +597,7 @@ class CSync cond.notify_all(); } - void signal_locked(UniqueLock& lk ATR_UNUSED) + void signal_locked(UniqueLock& lk SRT_ATR_UNUSED) { // EXPECTED: lk.mutex() is LOCKED. m_cond->notify_one(); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index caf882cd9..ed96306f5 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -18,7 +18,7 @@ written by // ATTRIBUTES: // -// ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) +// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) // ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) // ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode // ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. @@ -28,10 +28,8 @@ written by #ifdef __GNUG__ -#define ATR_UNUSED __attribute__((unused)) #define ATR_DEPRECATED __attribute__((deprecated)) #else -#define ATR_UNUSED #define ATR_DEPRECATED #endif #if defined(__cplusplus) && __cplusplus > 199711L @@ -719,7 +717,8 @@ struct CallbackHolder // Casting function-to-function, however, should not. Unfortunately // newer compilers disallow that, too (when a signature differs), but // then they should better use the C++11 way, much more reliable and safer. - void* (*testfn)(void*) ATR_UNUSED = (void*(*)(void*))f; + void* (*testfn)(void*) = (void*(*)(void*))f; + (void)(testfn); #endif opaque = o; fn = f; diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index 680ac6911..c8a441273 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -112,7 +112,7 @@ TEST(Core, ListenCallback) { (void)srt_cleanup(); } -int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid) +int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion, const struct sockaddr* peeraddr, const char* streamid) { using namespace std; diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index f986aa5dc..932406888 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -2260,7 +2260,7 @@ MediaPacket SrtSource::Read(size_t chunk) { static size_t counter = 1; - bool have_group ATR_UNUSED = !m_group_nodes.empty(); + bool have_group SRT_ATR_UNUSED = !m_group_nodes.empty(); bytevector data(chunk); // EXPERIMENTAL From 3c1c490715a6a6025bdda85a8ef1acf64616d8e8 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Fri, 2 Jul 2021 21:29:40 +0800 Subject: [PATCH 100/683] [tests] Fix -Wsign-compare --- test/test_buffer.cpp | 6 +++--- test/test_epoll.cpp | 4 ++-- test/test_fec_rebuilding.cpp | 6 +++--- test/test_ipv6.cpp | 2 +- test/test_seqno.cpp | 2 +- test/test_socket_options.cpp | 20 ++++++++++---------- test/test_sync.cpp | 6 +++--- test/test_utilities.cpp | 6 +++--- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 047ced7e2..d2e7b39f6 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -47,7 +47,7 @@ TEST(CRcvBuffer, FullBuffer) for (int i = 0; i < buffer_size_pkts - 1; ++i) { const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); } } @@ -109,7 +109,7 @@ TEST(CRcvBuffer, ReadData) std::array buff; const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); } @@ -148,7 +148,7 @@ TEST(CRcvBuffer, AddData) for (int i = 0; i < ack_pkts; ++i) { const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); + EXPECT_EQ(size_t(res), payload_size); EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - ack_pkts + i); } diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 8445450d3..8fb36be53 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -408,7 +408,7 @@ TEST(CEPoll, HandleEpollEvent2) int result = epoll.uwait(epoll_id, fds, 1024, -1); ASSERT_EQ(result, 1); - ASSERT_EQ(fds[0].events, SRT_EPOLL_ERR); + ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR)); // Edge-triggered means that after one wait call was done, the next // call to this event should no longer report it. Now use timeout 0 @@ -529,7 +529,7 @@ TEST(CEPoll, ThreadedUpdate) int result = epoll.uwait(epoll_id, fds, 1024, -1); cerr << "Exit no longer infinite-wait by uwait, result=" << result << "\n"; ASSERT_EQ(result, 1); - ASSERT_EQ(fds[0].events, SRT_EPOLL_ERR); + ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR)); cerr << "THREAD JOIN...\n"; td.join(); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 17f7cfbc9..1cfdf8ff9 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -801,7 +801,7 @@ TEST_F(TestFECRebuilding, NoRebuild) bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up - EXPECT_EQ(provided.size(), 0); // Confirm that nothing was rebuilt + EXPECT_EQ(provided.size(), 0U); // Confirm that nothing was rebuilt /* // XXX With such a short sequence, losses will not be reported. @@ -879,8 +879,8 @@ TEST_F(TestFECRebuilding, Rebuild) const bool want_passthru_fec = fec->receive(*fecpkt, loss); EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up - EXPECT_EQ(loss.size(), 0); - ASSERT_EQ(provided.size(), 1); + EXPECT_EQ(loss.size(), 0U); + ASSERT_EQ(provided.size(), 1U); SrtPacket& rebuilt = provided[0]; CPacket& skipped = *source[4]; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 677cae180..820288fa8 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -61,7 +61,7 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { - ASSERT_NE(fam.count(w.family()), 0) << "INVALID FAMILY"; + ASSERT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; } diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index 4a1cb0ec8..f3f50de62 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -27,7 +27,7 @@ TEST(CSeqNo, seqcmp) EXPECT_EQ(CSeqNo::seqcmp(1, 128), -127); // abs(seq1 - seq2) >= 0x3FFFFFFF : seq2 - seq1 - EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 1), 0x80000002); // -2147483646 + EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 1), int(0x80000002)); // -2147483646 EXPECT_EQ(CSeqNo::seqcmp(1, 0x7FFFFFFF), 0x7FFFFFFE); // 2147483646 } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 74a607838..d2e1da704 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -123,7 +123,7 @@ TEST_F(TestSocketOptions, LossMaxTTL) int opt_len = 0; ASSERT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS); EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the accepted socket"; - EXPECT_EQ(opt_len, sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the accepted socket"; + EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the accepted socket"; SRT_TRACEBSTATS stats; EXPECT_EQ(srt_bstats(accepted_sock, &stats, 0), SRT_SUCCESS); @@ -131,7 +131,7 @@ TEST_F(TestSocketOptions, LossMaxTTL) ASSERT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS); EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the listener socket"; - EXPECT_EQ(opt_len, sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the listener socket"; + EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the listener socket"; ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); } @@ -272,7 +272,7 @@ TEST_F(TestSocketOptions, StreamIDOdd) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_odd); - EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(size_t(buffer_len), sid_odd.size()); EXPECT_EQ(strlen(buffer), sid_odd.size()); StartListener(); @@ -283,7 +283,7 @@ TEST_F(TestSocketOptions, StreamIDOdd) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_odd.size()); + EXPECT_EQ(size_t(buffer_len), sid_odd.size()); EXPECT_EQ(strlen(buffer), sid_odd.size()); ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); @@ -301,7 +301,7 @@ TEST_F(TestSocketOptions, StreamIDEven) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_even); - EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(size_t(buffer_len), sid_even.size()); EXPECT_EQ(strlen(buffer), sid_even.size()); StartListener(); @@ -312,7 +312,7 @@ TEST_F(TestSocketOptions, StreamIDEven) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_even.size()); + EXPECT_EQ(size_t(buffer_len), sid_even.size()); EXPECT_EQ(strlen(buffer), sid_even.size()); ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); @@ -337,7 +337,7 @@ TEST_F(TestSocketOptions, StreamIDAlmostFull) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_amost_full); - EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_amost_full.size()); EXPECT_EQ(strlen(buffer), sid_amost_full.size()); StartListener(); @@ -348,7 +348,7 @@ TEST_F(TestSocketOptions, StreamIDAlmostFull) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_amost_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_amost_full.size()); EXPECT_EQ(strlen(buffer), sid_amost_full.size()); EXPECT_EQ(buffer[sid_amost_full.size()-1], 'z'); @@ -373,7 +373,7 @@ TEST_F(TestSocketOptions, StreamIDFull) int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); EXPECT_EQ(std::string(buffer), sid_full); - EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_full.size()); EXPECT_EQ(strlen(buffer), sid_full.size()); StartListener(); @@ -384,7 +384,7 @@ TEST_F(TestSocketOptions, StreamIDFull) buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, sid_full.size()); + EXPECT_EQ(size_t(buffer_len), sid_full.size()); EXPECT_EQ(strlen(buffer), sid_full.size()); EXPECT_EQ(buffer[sid_full.size()-1], 'z'); diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 4967fbf86..6fa59c137 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -461,7 +461,7 @@ TEST(SyncEvent, WaitForTwoNotifyOne) // Now exactly one waiting thread should become ready // Error if: 0 (none ready) or 2 (both ready, while notify_one was used) - ASSERT_EQ(notified_clients.size(), 1); + ASSERT_EQ(notified_clients.size(), 1U); const int ready = notified_clients[0]; const int not_ready = (ready + 1) % 2; @@ -611,7 +611,7 @@ TEST(Sync, FormatTime) const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6,}) \\[STDY\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); - EXPECT_LE(sm.size(), 6); + EXPECT_LE(sm.size(), 6U); if (sm.size() != 6 && sm.size() != 5) return 0; @@ -655,7 +655,7 @@ TEST(Sync, FormatTimeSys) const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYST\\]"); std::smatch sm; EXPECT_TRUE(regex_match(timestr, sm, rex)); - EXPECT_EQ(sm.size(), 5); + EXPECT_EQ(sm.size(), 5U); if (sm.size() != 5) return 0; diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp index 7deabe736..0ee56771c 100644 --- a/test/test_utilities.cpp +++ b/test/test_utilities.cpp @@ -219,7 +219,7 @@ TEST(ConfigString, Setting) StringStorage s; EXPECT_TRUE(s.empty()); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); EXPECT_EQ(s.str(), std::string()); char example_ac1[] = "example_long"; @@ -246,7 +246,7 @@ TEST(ConfigString, Setting) EXPECT_EQ(s.size(), sizeof (example_ac3)-1); EXPECT_TRUE(s.set(example_ace, sizeof (example_ace)-1)); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); string example_s1 = "example_long"; string example_s2 = "short"; @@ -268,6 +268,6 @@ TEST(ConfigString, Setting) EXPECT_EQ(s.size(), example_s3.size()); EXPECT_TRUE(s.set(example_se)); - EXPECT_EQ(s.size(), 0); + EXPECT_EQ(s.size(), 0U); EXPECT_TRUE(s.empty()); } From e8d890caf38c0ee851bf6c5a76da1bca407bd5db Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 16:18:53 +0200 Subject: [PATCH 101/683] [core] Moved mutex from CSndQueue to CSndUList Co-authored-by: Sektor van Skijlen --- CMakeLists.txt | 2 +- srtcore/queue.cpp | 51 +++++++++++++++++++++++++++-------------------- srtcore/queue.h | 35 +++++++++++++++----------------- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d360671..c14c81c55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,7 +129,7 @@ option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX librari option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) option(ENABLE_CODE_COVERAGE "Enable code coverage reporting" OFF) -option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV /temporary fix for #729/" OFF) +option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" OFF) option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" OFF) option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index b9d7ed02e..f17ad5745 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -255,20 +255,20 @@ void srt::CUnitQueue::makeUnitGood(CUnit* unit) unit->m_iFlag = CUnit::GOOD; } -srt::CSndUList::CSndUList() +srt::CSndUList::CSndUList(sync::CTimer* pTimer) : m_pHeap(NULL) , m_iArrayLength(512) , m_iLastEntry(-1) , m_ListLock() - , m_pWindowLock(NULL) - , m_pWindowCond(NULL) - , m_pTimer(NULL) + , m_pTimer(pTimer) { + setupCond(m_ListCond, "CSndUListCond"); m_pHeap = new CSNode*[m_iArrayLength]; } srt::CSndUList::~CSndUList() { + releaseCond(m_ListCond); delete[] m_pHeap; } @@ -305,7 +305,7 @@ int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) if (-1 == m_iLastEntry) return -1; - // no pop until the next schedulled time + // no pop until the next scheduled time if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) return -1; @@ -342,7 +342,6 @@ int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) void srt::CSndUList::remove(const CUDT* u) { ScopedLock listguard(m_ListLock); - remove_(u); } @@ -356,6 +355,21 @@ steady_clock::time_point srt::CSndUList::getNextProcTime() return m_pHeap[0]->m_tsTimeStamp; } +void srt::CSndUList::waitNonEmpty() const +{ + UniqueLock listguard(m_ListLock); + if (m_iLastEntry >= 0) + return; + + m_ListCond.wait(listguard); +} + +void srt::CSndUList::signalInterrupt() const +{ + ScopedLock listguard(m_ListLock); + m_ListCond.notify_all(); +} + void srt::CSndUList::realloc_() { CSNode** temp = NULL; @@ -420,7 +434,8 @@ void srt::CSndUList::insert_norealloc_(const steady_clock::time_point& ts, const // first entry, activate the sending queue if (0 == m_iLastEntry) { - CSync::lock_signal(*m_pWindowCond, *m_pWindowLock); + // m_ListLock is assumed to be locked. + m_ListCond.notify_all(); } } @@ -468,10 +483,8 @@ srt::CSndQueue::CSndQueue() : m_pSndUList(NULL) , m_pChannel(NULL) , m_pTimer(NULL) - , m_WindowCond() , m_bClosing(false) { - setupCond(m_WindowCond, "Window"); } srt::CSndQueue::~CSndQueue() @@ -483,14 +496,14 @@ srt::CSndQueue::~CSndQueue() m_pTimer->interrupt(); } - CSync::lock_signal(m_WindowCond, m_WindowLock); + // Unblock CSndQueue worker thread if it is waiting. + m_pSndUList->signalInterrupt(); if (m_WorkerThread.joinable()) { HLOGC(rslog.Debug, log << "SndQueue: EXIT"); m_WorkerThread.join(); } - releaseCond(m_WindowCond); delete m_pSndUList; } @@ -510,12 +523,9 @@ int srt::CSndQueue::m_counter = 0; void srt::CSndQueue::init(CChannel* c, CTimer* t) { - m_pChannel = c; - m_pTimer = t; - m_pSndUList = new CSndUList; - m_pSndUList->m_pWindowLock = &m_WindowLock; - m_pSndUList->m_pWindowCond = &m_WindowCond; - m_pSndUList->m_pTimer = m_pTimer; + m_pChannel = c; + m_pTimer = t; + m_pSndUList = new CSndUList(t); #if ENABLE_LOGGING ++m_counter; @@ -575,14 +585,11 @@ void* srt::CSndQueue::worker(void* param) self->m_WorkerStats.lNotReadyTs++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ - UniqueLock windlock(self->m_WindowLock); - CSync windsync(self->m_WindowCond, windlock); - // wait here if there is no sockets with data to be sent THREAD_PAUSED(); - if (!self->m_bClosing && (self->m_pSndUList->m_iLastEntry < 0)) + if (!self->m_bClosing) { - windsync.wait(); + self->m_pSndUList->waitNonEmpty(); #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lCondWait++; diff --git a/srtcore/queue.h b/srtcore/queue.h index ee05440c8..77256c3fc 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -157,10 +157,8 @@ struct CSNode class CSndUList { - friend class CSndQueue; - public: - CSndUList(); + CSndUList(sync::CTimer* pTimer); ~CSndUList(); public: @@ -175,30 +173,32 @@ class CSndUList /// Update the timestamp of the UDT instance on the list. /// @param [in] u pointer to the UDT instance /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. /// @param [out] addr destination address of the next packet /// @param [out] pkt the next packet to be sent /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); /// Remove UDT instance from the list. /// @param [in] u pointer to the UDT instance - - void remove(const CUDT* u); + void remove(const CUDT* u);// EXCLUDES(m_ListLock); /// Retrieve the next scheduled processing time. /// @return Scheduled processing time of the first UDT socket in the list. - sync::steady_clock::time_point getNextProcTime(); + /// Wait for the list to become non empty. + void waitNonEmpty() const; + + /// Signal to stop waiting in waitNonEmpty(). + void signalInterrupt() const; + private: /// Doubles the size of the list. /// - void realloc_(); + void realloc_();// REQUIRES(m_ListLock); /// Insert a new UDT instance into the list with realloc if required. /// @@ -211,21 +211,21 @@ class CSndUList /// /// @param [in] ts time stamp: next processing time /// @param [in] u pointer to the UDT instance - void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u); + void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u);// REQUIRES(m_ListLock); + /// Removes CUDT entry from the list. + /// If the last entry is removed, calls sync::CTimer::interrupt(). void remove_(const CUDT* u); private: CSNode** m_pHeap; // The heap array int m_iArrayLength; // physical length of the array - int m_iLastEntry; // position of last entry on the heap array + int m_iLastEntry; // position of last entry on the heap array or -1 if empty. - sync::Mutex m_ListLock; + mutable sync::Mutex m_ListLock; // Protects the list (m_pHeap, m_iArrayLength, m_iLastEntry). + mutable sync::Condition m_ListCond; - sync::Mutex* m_pWindowLock; - sync::Condition* m_pWindowCond; - - sync::CTimer* m_pTimer; + sync::CTimer* const m_pTimer; private: CSndUList(const CSndUList&); @@ -462,9 +462,6 @@ class CSndQueue CChannel* m_pChannel; // The UDP channel for data sending srt::sync::CTimer* m_pTimer; // Timing facility - srt::sync::Mutex m_WindowLock; - srt::sync::Condition m_WindowCond; - srt::sync::atomic m_bClosing; // closing the worker #if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker From e9c550b0e7c26ff80d7ad80d2ad2ab916d249c08 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Jun 2021 17:09:27 +0200 Subject: [PATCH 102/683] [core] Refactor CSndUList: pop CUDT, not a packet --- srtcore/core.cpp | 4 +-- srtcore/queue.cpp | 82 ++++++++++++++++++++++++++--------------------- srtcore/queue.h | 11 +++---- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 15a138a3a..14dd2c888 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7974,14 +7974,14 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) #endif // insert this socket to snd list if it is not on the list yet - m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); + const steady_clock::time_point currtime = steady_clock::now(); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE, currtime); if (m_config.bSynSending) { CSync::lock_signal(m_SendBlockCond, m_SendBlockLock); } - const steady_clock::time_point currtime = steady_clock::now(); // record total time used for sending enterCS(m_StatsLock); m_stats.sndDuration += count_microseconds(currtime - m_stats.sndDurationCounter); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index f17ad5745..8cefd276d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -272,7 +272,7 @@ srt::CSndUList::~CSndUList() delete[] m_pHeap; } -void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) +void srt::CSndUList::update(const CUDT* u, EReschedule reschedule, sync::steady_clock::time_point ts) { ScopedLock listguard(m_ListLock); @@ -285,58 +285,33 @@ void srt::CSndUList::update(const CUDT* u, EReschedule reschedule) if (n->m_iHeapLoc == 0) { - n->m_tsTimeStamp = steady_clock::now(); + n->m_tsTimeStamp = ts; m_pTimer->interrupt(); return; } remove_(u); - insert_norealloc_(steady_clock::now(), u); + insert_norealloc_(ts, u); return; } - insert_(steady_clock::now(), u); + insert_(ts, u); } -int srt::CSndUList::pop(sockaddr_any& w_addr, CPacket& w_pkt) +srt::CUDT* srt::CSndUList::pop() { ScopedLock listguard(m_ListLock); if (-1 == m_iLastEntry) - return -1; + return NULL; // no pop until the next scheduled time if (m_pHeap[0]->m_tsTimeStamp > steady_clock::now()) - return -1; + return NULL; CUDT* u = m_pHeap[0]->m_pUDT; remove_(u); - -#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " - - HLOGC(qslog.Debug, - log << "SND:pop: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) - << UST(Connecting) << UST(Connected) << UST(Closing) << UST(Shutdown) << UST(Broken) << UST(PeerHealth) - << UST(Opened)); -#undef UST - - if (!u->m_bConnected || u->m_bBroken) - return -1; - - // pack a packet from the socket - const std::pair res_time = u->packData((w_pkt)); - - if (res_time.first <= 0) - return -1; - - w_addr = u->m_PeerAddr; - - // insert a new entry, ts is the next processing time - const steady_clock::time_point send_time = res_time.second; - if (!is_zero(send_time)) - insert_norealloc_(send_time, u); - - return 1; + return u; } void srt::CSndUList::remove(const CUDT* u) @@ -630,18 +605,51 @@ void* srt::CSndQueue::worker(void* param) } THREAD_RESUMED(); - // it is time to send the next pkt - sockaddr_any addr; - CPacket pkt; - if (self->m_pSndUList->pop((addr), (pkt)) < 0) + // Get a socket with a send request if any. + CUDT* u = self->m_pSndUList->pop(); + if (u == NULL) { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyPop++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ continue; + } + +#define UST(field) ((u->m_b##field) ? "+" : "-") << #field << " " + HLOGC(qslog.Debug, + log << "CSndQueue: requesting packet from @" << u->socketID() << " STATUS: " << UST(Listening) + << UST(Connecting) << UST(Connected) << UST(Closing) << UST(Shutdown) << UST(Broken) << UST(PeerHealth) + << UST(Opened)); +#undef UST + if (!u->m_bConnected || u->m_bBroken) + { #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lNotReadyPop++; #endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + continue; } + // pack a packet from the socket + CPacket pkt; + const std::pair res_time = u->packData((pkt)); + + // Check if payload size is invalid. + if (res_time.first <= 0) + { +#if defined(SRT_DEBUG_SNDQ_HIGHRATE) + self->m_WorkerStats.lNotReadyPop++; +#endif /* SRT_DEBUG_SNDQ_HIGHRATE */ + continue; + } + + const sockaddr_any addr = u->m_PeerAddr; + // Insert a new entry, send_time is the next processing time. + // TODO: maybe reschedule by taking the smaller time? + const steady_clock::time_point send_time = res_time.second; + if (!is_zero(send_time)) + self->m_pSndUList->update(u, CSndUList::DONT_RESCHEDULE, send_time); + HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info()); self->m_pChannel->sendto(addr, pkt); diff --git a/srtcore/queue.h b/srtcore/queue.h index 77256c3fc..2b7408747 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -173,13 +173,12 @@ class CSndUList /// Update the timestamp of the UDT instance on the list. /// @param [in] u pointer to the UDT instance /// @param [in] reschedule if the timestamp should be rescheduled - void update(const CUDT* u, EReschedule reschedule); + /// @param [in] ts the next time to trigger sending logic on the CUDT + void update(const CUDT* u, EReschedule reschedule, sync::steady_clock::time_point ts = sync::steady_clock::now()); - /// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue. - /// @param [out] addr destination address of the next packet - /// @param [out] pkt the next packet to be sent - /// @return 1 if successfully retrieved, -1 if no packet found. - int pop(sockaddr_any& addr, CPacket& pkt); + /// Retrieve the next (in time) socket from the heap to process its sending request. + /// @return a pointer to CUDT instance to process next. + CUDT* pop(); /// Remove UDT instance from the list. /// @param [in] u pointer to the UDT instance From 96a41db2e0a7f53f9b16a0f823663892afadb1b3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Jul 2021 16:47:07 +0200 Subject: [PATCH 103/683] [core] CSndUList::update: reschedule if earlier time --- srtcore/queue.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8cefd276d..2b0fd2c74 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -280,7 +280,10 @@ void srt::CSndUList::update(const CUDT* u, EReschedule reschedule, sync::steady_ if (n->m_iHeapLoc >= 0) { - if (!reschedule) // EReschedule to bool conversion, predicted. + if (reschedule == DONT_RESCHEDULE) + return; + + if (n->m_tsTimeStamp <= ts) return; if (n->m_iHeapLoc == 0) @@ -644,11 +647,9 @@ void* srt::CSndQueue::worker(void* param) } const sockaddr_any addr = u->m_PeerAddr; - // Insert a new entry, send_time is the next processing time. - // TODO: maybe reschedule by taking the smaller time? - const steady_clock::time_point send_time = res_time.second; - if (!is_zero(send_time)) - self->m_pSndUList->update(u, CSndUList::DONT_RESCHEDULE, send_time); + const steady_clock::time_point next_send_time = res_time.second; + if (!is_zero(next_send_time)) + self->m_pSndUList->update(u, CSndUList::DO_RESCHEDULE, next_send_time); HLOGC(qslog.Debug, log << self->CONID() << "chn:SENDING: " << pkt.Info()); self->m_pChannel->sendto(addr, pkt); From 65ae2575798595d209f4d57452b0b990e7295d55 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 20 Jul 2021 11:56:49 +0200 Subject: [PATCH 104/683] [core] Added missing RCV buffer lock in CUDT::receiveBuffer(..). Co-authored-by: Maxim Sharabayko --- srtcore/core.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 14dd2c888..65b51e850 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6108,7 +6108,9 @@ int srt::CUDT::receiveBuffer(char *data, int len) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } + enterCS(m_RcvBufferLock); const int res = m_pRcvBuffer->readBuffer(data, len); + leaveCS(m_RcvBufferLock); /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -7056,7 +7058,9 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo } unitsize = int((torecv > block) ? block : torecv); + enterCS(m_RcvBufferLock); recvsize = m_pRcvBuffer->readBufferToFile(ofs, unitsize); + leaveCS(m_RcvBufferLock); if (recvsize > 0) { From ec24e15d50778844989676b4d36476d1c88f7bd5 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 21 Jul 2021 11:48:23 +0800 Subject: [PATCH 105/683] [core] Replace hacky static_assert by SRT_STATIC_ASSERT Remove local static SRTDATA_MAXSIZE since there is another one in global namespace. --- srtcore/core.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 65b51e850..75c0171ae 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1208,16 +1208,9 @@ void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) CPacket srtpkt; int32_t srtcmd = (int32_t)cmd; - static const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ / sizeof(int32_t); - - // This is in order to issue a compile error if the SRT_CMD_MAXSZ is - // too small to keep all the data. As this is "static const", declaring - // an array of such specified size in C++ isn't considered VLA. - static const int SRTDATA_SIZE = SRTDATA_MAXSIZE >= SRT_HS_E_SIZE ? SRTDATA_MAXSIZE : -1; - - // This will be effectively larger than SRT_HS_E_SIZE, but it will be also used - // for incoming data. We have a guarantee that it won't be larger than SRTDATA_MAXSIZE. - uint32_t srtdata[SRTDATA_SIZE]; + SRT_STATIC_ASSERT(SRTDATA_MAXSIZE >= SRT_HS_E_SIZE, "SRT_CMD_MAXSZ is too small to hold all the data"); + // This will be effectively larger than SRT_HS_E_SIZE, but it will be also used for incoming data. + uint32_t srtdata[SRTDATA_MAXSIZE]; size_t srtlen = 0; @@ -1233,7 +1226,7 @@ void srt::CUDT::sendSrtMsg(int cmd, uint32_t *srtdata_in, size_t srtlen_in) { case SRT_CMD_HSREQ: case SRT_CMD_HSRSP: - srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_SIZE); + srtlen = prepareSrtHsMsg(cmd, srtdata, SRTDATA_MAXSIZE); break; case SRT_CMD_KMREQ: // Sender From 1df29db15d671c91ea8e1dc30c263cd39e8138ad Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 21 Jul 2021 11:56:54 +0800 Subject: [PATCH 106/683] [core] use sizeof(uint32_t) in SRTDATA_MAXSIZE Since all usecase of SRTDATA_MAXSIZE are array of uint32_t. --- srtcore/crypto.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 4e067678b..558f229f2 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -48,7 +48,7 @@ namespace srt const size_t SRT_KMR_KMSTATE = 0; #define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */ -const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(int32_t); +const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t); enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1}; From a34aa086060071e024076cec05f1b24ec7834416 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 22 Jul 2021 12:17:22 +0800 Subject: [PATCH 107/683] [core] Set CLOEXEC for epoll on Linux epoll_create1() was added to the kernel in version 2.6.27. Library support is provided in glibc starting with version 2.9 which was released at the year of 2008. --- srtcore/epoll.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 548418f88..5755c9dff 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -108,7 +108,11 @@ int CEPoll::create(CEPollDesc** pout) int localid = 0; #ifdef LINUX - localid = epoll_create(1024); + int flags = 0; +#if ENABLE_SOCK_CLOEXEC + flags |= EPOLL_CLOEXEC; +#endif + localid = epoll_create1(flags); /* Possible reasons of -1 error: EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered. ENFILE: The system limit on the total number of open files has been reached. From 2ca53138a3669c192d2f9f8166c9749941f10dae Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Jul 2021 15:07:01 +0200 Subject: [PATCH 108/683] [core] Added Clang TSA attributes. (#2000) - Added CMake build option ENABLE_CLANG_TSA. - srt::sync::UniqueLock now may throw an exception in unlock and lock. - MSVC SAL attributes are partially added. --- CMakeLists.txt | 7 +++ srtcore/logging.h | 3 + srtcore/platform_sys.h | 2 +- srtcore/srt.h | 2 +- srtcore/srt_attr_defs.h | 119 ++++++++++++++++++++++++++++++++++++++++ srtcore/sync.h | 46 ++++++++++------ srtcore/sync_posix.cpp | 21 ++++--- srtcore/udt.h | 4 +- 8 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 srtcore/srt_attr_defs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c14c81c55..f210ff34f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,8 @@ option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potenti option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) + set(TARGET_srt "srt" CACHE STRING "The name for the SRT library") # Use application-defined group reader @@ -634,6 +636,11 @@ if (ENABLE_THREAD_CHECK) ) endif() +if (ENABLE_CLANG_TSA) + list(APPEND SRT_EXTRA_CFLAGS "-Wthread-safety") + message(STATUS "Clang TSA: Enabled") +endif() + if (ENABLE_PROFILE) if (HAVE_COMPILER_GNU_COMPAT) # They are actually cflags, not definitions, but CMake is stupid enough. diff --git a/srtcore/logging.h b/srtcore/logging.h index 8767f2aa7..5669c8f45 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -131,7 +131,10 @@ struct LogConfig { } + SRT_ATTR_ACQUIRE(mutex) void lock() { mutex.lock(); } + + SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } }; diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index 9fa2c7d32..79760080e 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -41,7 +41,7 @@ #include #include #if defined(_MSC_VER) - #pragma warning(disable:4251) + #pragma warning(disable: 4251 26812) #endif #else diff --git a/srtcore/srt.h b/srtcore/srt.h index 5d3349a48..496112249 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -963,7 +963,7 @@ typedef struct SRT_EPOLL_EVENT_STR int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR #ifdef __cplusplus SRT_EPOLL_EVENT_STR(SRTSOCKET s, int ev): fd(s), events(ev) {} - SRT_EPOLL_EVENT_STR() {} // NOTE: allows singular values, no init. + SRT_EPOLL_EVENT_STR(): fd(-1), events(0) {} // NOTE: allows singular values, no init. #endif } SRT_EPOLL_EVENT; SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h new file mode 100644 index 000000000..24bc8bc84 --- /dev/null +++ b/srtcore/srt_attr_defs.h @@ -0,0 +1,119 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2019 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +/***************************************************************************** +The file contains various planform and compiler dependent attribute definitions +used by SRT library internally. + +1. Attributes for thread safety analysis + - Clang (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). + - Other compilers: none. + + *****************************************************************************/ + +#ifndef INC_SRT_ATTR_DEFS_H +#define INC_SRT_ATTR_DEFS_H + +#if _MSC_VER >= 1920 +// In case of MSVC these attributes have to preceed the attributed objects (variable, function). +// E.g. SRT_ATTR_GUARDED_BY(mtx) int object; +// It is tricky to annotate e.g. the following function, as clang complaints it does not know 'm'. +// SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) +// inline void enterCS(Mutex& m) { m.lock(); } +#define SRT_ATTR_CAPABILITY(expr) +#define SRT_ATTR_SCOPED_CAPABILITY +#define SRT_ATTR_GUARDED_BY(expr) _Guarded_by_(expr) +#define SRT_ATTR_PT_GUARDED_BY(expr) +#define SRT_ATTR_ACQUIRED_BEFORE(...) +#define SRT_ATTR_ACQUIRED_AFTER(...) +#define SRT_ATTR_REQUIRES(expr) _Requires_lock_held_(expr) +#define SRT_ATTR_REQUIRES_SHARED(...) +#define SRT_ATTR_ACQUIRE(expr) _Acquires_nonreentrant_lock_(expr) +#define SRT_ATTR_ACQUIRE_SHARED(...) +#define SRT_ATTR_RELEASE(expr) _Releases_lock_(expr) +#define SRT_ATTR_RELEASE_SHARED(...) +#define SRT_ATTR_RELEASE_GENERIC(...) +#define SRT_ATTR_TRY_ACQUIRE(...) _Acquires_nonreentrant_lock_(expr) +#define SRT_ATTR_TRY_ACQUIRE_SHARED(...) +#define SRT_ATTR_EXCLUDES(...) +#define SRT_ATTR_ASSERT_CAPABILITY(expr) +#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) +#define SRT_ATTR_RETURN_CAPABILITY(x) +#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS +#else + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define SRT_ATTR_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define SRT_ATTR_SCOPED_CAPABILITY \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +#define SRT_ATTR_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#define SRT_ATTR_PT_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +#define SRT_ATTR_ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define SRT_ATTR_REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define SRT_ATTR_REQUIRES_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define SRT_ATTR_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_RELEASE_GENERIC(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) + +#define SRT_ATTR_TRY_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define SRT_ATTR_TRY_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define SRT_ATTR_EXCLUDES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +#define SRT_ATTR_ASSERT_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +#define SRT_ATTR_RETURN_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif // not _MSC_VER + +#endif // INC_SRT_ATTR_DEFS_H diff --git a/srtcore/sync.h b/srtcore/sync.h index 7da6952f7..081cb41e4 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -53,6 +53,7 @@ #include "srt.h" #include "utilities.h" +#include "srt_attr_defs.h" class CUDTException; // defined in common.h @@ -379,7 +380,7 @@ using ScopedLock = lock_guard; /// Mutex is a class wrapper, that should mimic the std::chrono::mutex class. /// At the moment the extra function ref() is temporally added to allow calls /// to pthread_cond_timedwait(). Will be removed by introducing CEvent. -class Mutex +class SRT_ATTR_CAPABILITY("mutex") Mutex { friend class SyncEvent; @@ -388,11 +389,11 @@ class Mutex ~Mutex(); public: - int lock(); - int unlock(); + int lock() SRT_ATTR_ACQUIRE(); + int unlock() SRT_ATTR_RELEASE(); /// @return true if the lock was acquired successfully, otherwise false - bool try_lock(); + bool try_lock() SRT_ATTR_TRY_ACQUIRE(true); // TODO: To be removed with introduction of the CEvent. pthread_mutex_t& ref() { return m_mutex; } @@ -402,10 +403,13 @@ class Mutex }; /// A pthread version of std::chrono::scoped_lock (or lock_guard for C++11) -class ScopedLock +class SRT_ATTR_SCOPED_CAPABILITY ScopedLock { public: + SRT_ATTR_ACQUIRE(m) ScopedLock(Mutex& m); + + SRT_ATTR_RELEASE() ~ScopedLock(); private: @@ -413,17 +417,25 @@ class ScopedLock }; /// A pthread version of std::chrono::unique_lock -class UniqueLock +class SRT_ATTR_SCOPED_CAPABILITY UniqueLock { friend class SyncEvent; public: + SRT_ATTR_ACQUIRE(m_Mutex) UniqueLock(Mutex &m); + + SRT_ATTR_RELEASE(m_Mutex) ~UniqueLock(); public: + SRT_ATTR_ACQUIRE(m_Mutex) void lock(); + + SRT_ATTR_RELEASE(m_Mutex) void unlock(); + + SRT_ATTR_RETURN_CAPABILITY(m_Mutex) Mutex* mutex(); // reflects C++11 unique_lock::mutex() private: @@ -432,26 +444,28 @@ class UniqueLock }; #endif // ENABLE_STDCXX_SYNC -inline void enterCS(Mutex& m) { m.lock(); } -inline bool tryEnterCS(Mutex& m) { return m.try_lock(); } -inline void leaveCS(Mutex& m) { m.unlock(); } +inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); } + +inline bool tryEnterCS(Mutex& m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } + +inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); } class InvertedLock { - Mutex *m_pMutex; + Mutex& m_mtx; - public: +public: + SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) InvertedLock(Mutex& m) - : m_pMutex(&m) + : m_mtx(m) { - leaveCS(*m_pMutex); + m_mtx.unlock(); } + SRT_ATTR_ACQUIRE(m_mtx) ~InvertedLock() { - if (!m_pMutex) - return; - enterCS(*m_pMutex); + m_mtx.lock(); } }; diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 0972dd731..960a1dfad 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -244,22 +244,27 @@ srt::sync::UniqueLock::UniqueLock(Mutex& m) srt::sync::UniqueLock::~UniqueLock() { - unlock(); + if (m_iLocked == 0) + { + unlock(); + } } void srt::sync::UniqueLock::lock() { - if (m_iLocked == -1) - m_iLocked = m_Mutex.lock(); + if (m_iLocked != -1) + throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0); + + m_iLocked = m_Mutex.lock(); } void srt::sync::UniqueLock::unlock() { - if (m_iLocked == 0) - { - m_Mutex.unlock(); - m_iLocked = -1; - } + if (m_iLocked != 0) + throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0); + + m_Mutex.unlock(); + m_iLocked = -1; } srt::sync::Mutex* srt::sync::UniqueLock::mutex() diff --git a/srtcore/udt.h b/srtcore/udt.h index b1af441b2..60bc8d07d 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -70,10 +70,10 @@ modified by #include "srt.h" /* -* SRT_ENABLE_THREADCHECK (THIS IS SET IN MAKEFILE NOT HERE) +* SRT_ENABLE_THREADCHECK IS SET IN MAKEFILE, NOT HERE */ #if defined(SRT_ENABLE_THREADCHECK) -#include +#include "threadcheck.h" #else #define THREAD_STATE_INIT(name) #define THREAD_EXIT() From d5d4b18f3efc59617b47e8dc6ca9b40b62872e93 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 26 Jul 2021 09:24:22 +0200 Subject: [PATCH 109/683] [docs] Fixed a link in Configuration Guidelines --- docs/API/configuration-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index f0566d7d0..1e6d2111e 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -6,7 +6,7 @@ The receiver buffer can be configured with the [`SRTO_RCVBUF`](./API-socket-opti Buffer size in bytes is expected to be passed in the `optval` argument of the `srt_setsockopt(..)` function. However, internally the value will be converted into the number of packets stored in the receiver buffer. -The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`]((./API-socket-options.md#SRTO_FC) socket option. +The allowed value of `SRTO_RCVBUF` is also limited by the value of the flow control window size [`SRTO_FC`](./API-socket-options.md#SRTO_FC) socket option. See issue [#700](https://github.com/Haivision/srt/issues/700). The default flow control window size is 25600 packets. It is approximately: From 589f103926e9297fac4cd7f2c1e9dbdde9d8925c Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 5 Jul 2021 18:46:21 +0800 Subject: [PATCH 110/683] [core] Move CSrtConfigSetter templates to cpp file They are only used by socketconfig.cpp, socketconfig.h is included by multiple files. --- srtcore/socketconfig.cpp | 818 ++++++++++++++++++++++++++++++++++++++ srtcore/socketconfig.h | 829 --------------------------------------- 2 files changed, 818 insertions(+), 829 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index e50bb08f5..8cb06a08f 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -54,6 +54,824 @@ written by extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); +typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); + +template +struct CSrtConfigSetter +{ + static setter_function set; +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int ival = cast_optval(optval, optlen); + if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iMSS = ival; + + // Packet size cannot be greater than UDP buffer size + if (co.iMSS > co.iUDPSndBufSize) + co.iMSS = co.iUDPSndBufSize; + if (co.iMSS > co.iUDPRcvBufSize) + co.iMSS = co.iUDPRcvBufSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + const int fc = cast_optval(optval, optlen); + if (fc < co.DEF_MIN_FLIGHT_PKT) + { + LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL); + } + + co.iFlightFlagSize = fc; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int bs = cast_optval(optval, optlen); + if (bs <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val <= 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + // Mimimum recv buffer size is 32 packets + const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; + + if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) + co.iRcvBufSize = val / mssin_size; + else + co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; + + // recv buffer MUST not be greater than FC size + if (co.iRcvBufSize > co.iFlightFlagSize) + co.iRcvBufSize = co.iFlightFlagSize; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.Linger = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iUDPSndBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iUDPRcvBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bRendezvous = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndTimeOut = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvTimeOut = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bSynSending = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bSynRecving = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bReuseAddr = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.llMaxBW = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + int val = cast_optval(optval, optlen); + if (!(val == -1) && !((val >= 1) && (val <= 255))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.iIpTTL = cast_optval(optval); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iIpToS = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_BINDTODEVICE + using namespace std; + using namespace srt_logging; + + string val; + if (optlen == -1) + val = (const char *)optval; + else + val.assign((const char *)optval, optlen); + if (val.size() >= IFNAMSIZ) + { + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.sBindToDevice = val; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.llInputBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int64_t val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.llMinInputBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int32_t val = cast_optval(optval, optlen); + if (val < 5 || val > 100) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + co.iOverheadBW = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bDataSender = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bTSBPD = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + co.iPeerLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRcvLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerLatency = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bTLPktDrop = cast_optval(optval, optlen); + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < -1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iSndDropDelay = val; + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + // Password must be 10-80 characters. + // Or it can be empty to clear the password. + if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + memset(&co.CryptoSecret, 0, sizeof(co.CryptoSecret)); + co.CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; + co.CryptoSecret.len = (optlen <= (int)sizeof(co.CryptoSecret.str) ? optlen : (int)sizeof(co.CryptoSecret.str)); + memcpy((co.CryptoSecret.str), optval, co.CryptoSecret.len); +#else + (void)co; // prevent warning + (void)optval; + if (optlen == 0) + return; // Allow to set empty passphrase if no encryption supported. + + LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; +#ifdef SRT_ENABLE_ENCRYPTION + const int v = cast_optval(optval, optlen); + int const allowed[4] = { + 0, // Default value, if this results for initiator, defaults to 16. See below. + 16, // AES-128 + 24, // AES-192 + 32 // AES-256 + }; + const int *const allowed_end = allowed + 4; + if (std::find(allowed, allowed_end, v) == allowed_end) + { + LOGC(aclog.Error, + log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Note: This works a little different in HSv4 and HSv5. + + // HSv4: + // The party that is set SRTO_SENDER will send KMREQ, and it will + // use default value 16, if SRTO_PBKEYLEN is the default value 0. + // The responder that receives KMRSP has nothing to say about + // PBKEYLEN anyway and it will take the length of the key from + // the initiator (sender) as a good deal. + // + // HSv5: + // The initiator (independently on the sender) will send KMREQ, + // and as it should be the sender to decide about the PBKEYLEN. + // Your application should do the following then: + // 1. The sender should set PBKEYLEN to the required value. + // 2. If the sender is initiator, it will create the key using + // its preset PBKEYLEN (or default 16, if not set) and the + // receiver-responder will take it as a good deal. + // 3. Leave the PBKEYLEN value on the receiver as default 0. + // 4. If sender is responder, it should then advertise the PBKEYLEN + // value in the initial handshake messages (URQ_INDUCTION if + // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case + // of rendezvous, as it is the matter of luck who of them will + // eventually become the initiator). This way the receiver + // being an initiator will set iSndCryptoKeyLen before setting + // up KMREQ for sending to the sender-responder. + // + // Note that in HSv5 if both sides set PBKEYLEN, the responder + // wins, unless the initiator is a sender (the effective PBKEYLEN + // will be the one advertised by the responder). If none sets, + // PBKEYLEN will default to 16. + + co.iSndCryptoKeyLen = v; +#else + (void)co; // prevent warning + (void)optval; + (void)optlen; + LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bRcvNakReport = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + using namespace srt::sync; + co.tdConnTimeOut = milliseconds_from(val); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bDriftTracer = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iMaxReorderTolerance = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.uSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.uMinimumPeerSrtVersion = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.sStreamName.set((const char*)optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + std::string val; + if (optlen == -1) + val = (const char*)optval; + else + val.assign((const char*)optval, optlen); + + // Translate alias + if (val == "vod") + val = "file"; + + bool res = SrtCongestion::exists(val); + if (!res) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.sCongestion.set(val); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bMessageAPI = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + const int val = cast_optval(optval, optlen); + if (val < 0) + { + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (val > SRT_LIVE_MAX_PLSIZE) + { + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (!co.sPacketFilterConfig.empty()) + { + // This means that the filter might have been installed before, + // and the fix to the maximum payload size was already applied. + // This needs to be checked now. + srt::SrtFilterConfig fc; + if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) + { + // Break silently. This should not happen + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (size_t(val) > efc_max_payload_size) + { + LOGC(aclog.Error, + log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size + << " required for packet filter header"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + co.zExpPayloadSize = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + // XXX Note that here the configuration for SRTT_LIVE + // is the same as DEFAULT VALUES for these fields set + // in CUDT::CUDT. + switch (cast_optval(optval, optlen)) + { + case SRTT_LIVE: + // Default live options: + // - tsbpd: on + // - latency: 120ms + // - linger: off + // - congctl: live + // - extraction method: message (reading call extracts one message) + co.bTSBPD = true; + co.iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; + co.iPeerLatency = 0; + co.bTLPktDrop = true; + co.iSndDropDelay = 0; + co.bMessageAPI = true; + co.bRcvNakReport = true; + co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; + co.Linger.l_onoff = 0; + co.Linger.l_linger = 0; + co.sCongestion.set("live", 4); + break; + + case SRTT_FILE: + // File transfer mode: + // - tsbpd: off + // - latency: 0 + // - linger: on + // - congctl: file (original UDT congestion control) + // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) + co.bTSBPD = false; + co.iRcvLatency = 0; + co.iPeerLatency = 0; + co.bTLPktDrop = false; + co.iSndDropDelay = -1; + co.bMessageAPI = false; + co.bRcvNakReport = false; + co.zExpPayloadSize = 0; // use maximum + co.Linger.l_onoff = 1; + co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; + co.sCongestion.set("file", 4); + break; + + default: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iGroupConnect = cast_optval(optval, optlen); + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value + co.uKmRefreshRatePkt = (unsigned) val; + + if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) + return; // Both values are default + + const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; + const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + + if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) + { + co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; + LOGC(aclog.Warn, + log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" + << std::hex << co.uKmPreAnnouncePkt); + } + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + + const int val = cast_optval(optval, optlen); + if (val < 0) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; + const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; + if (km_preanno > (kmref - 1) / 2) + { + LOGC(aclog.Error, + log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + << " - OPTION REJECTED."); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.uKmPreAnnouncePkt = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.bEnforcedEnc = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + const int val = cast_optval(optval, optlen); + if (val < 0) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iPeerIdleTimeout = val; + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iIpV6Only = cast_optval(optval, optlen); + } +}; + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + std::string arg((const char*)optval, optlen); + // Parse the configuration string prematurely + srt::SrtFilterConfig fc; + srt::PacketFilter::Factory* fax = 0; + if (!srt::ParseFilterConfig(arg, (fc), (&fax))) + { + LOGC(aclog.Error, + log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " + "FILTERTYPE (" + << fc.type << ") must be installed (or builtin)"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + std::string error; + if (!fax->verifyConfig(fc, (error))) + { + LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect config: " << error); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + if (co.zExpPayloadSize > efc_max_payload_size) + { + LOGC(aclog.Warn, + log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " + << efc_max_payload_size << " bytes"); + co.zExpPayloadSize = efc_max_payload_size; + } + + co.sPacketFilterConfig.set(arg); + } +}; + +#if ENABLE_EXPERIMENTAL_BONDING +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + using namespace srt_logging; + // This option is meaningless for the socket itself. + // It's set here just for the sake of setting it on a listener + // socket so that it is then applied on the group when a + // group connection is configuired. + const int val = cast_optval(optval, optlen); + + // Search if you already have SRTO_PEERIDLETIMEO set + + const int idletmo = co.iPeerIdleTimeout; + + // Both are in milliseconds. + // This option is RECORDED in microseconds, while + // idletmo is recorded in milliseconds, only translated to + // microseconds directly before use. + if (val >= idletmo) + { + LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val + << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + co.uStabilityTimeout = val * 1000; + } +}; +#endif + +template<> +struct CSrtConfigSetter +{ + static void set(CSrtConfig& co, const void* optval, int optlen) + { + co.iRetransmitAlgo = cast_optval(optval, optlen); + } +}; + static struct SrtConfigSetter { setter_function* fn[SRTO_E_SIZE]; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 72b90195e..a9d2a0856 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -116,14 +116,6 @@ struct CSrtMuxerConfig struct CSrtConfig; -typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); - -template -struct CSrtConfigSetter -{ - static setter_function set; -}; - template class StringStorage { @@ -314,14 +306,6 @@ struct CSrtConfig: CSrtMuxerConfig } int set(SRT_SOCKOPT optName, const void* val, int size); - - // Could be later made a more robust version with - // dispatching to the right data type. - template - void set(const void* val, int size) - { - CSrtConfigSetter::set(*this, val, size); - } }; @@ -390,817 +374,4 @@ inline bool cast_optval(const void* optval, int optlen) return false; } -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int ival = cast_optval(optval, optlen); - if (ival < int(srt::CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iMSS = ival; - - // Packet size cannot be greater than UDP buffer size - if (co.iMSS > co.iUDPSndBufSize) - co.iMSS = co.iUDPSndBufSize; - if (co.iMSS > co.iUDPRcvBufSize) - co.iMSS = co.iUDPRcvBufSize; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - const int fc = cast_optval(optval, optlen); - if (fc < co.DEF_MIN_FLIGHT_PKT) - { - LOGC(kmlog.Error, log << "SRTO_FC: minimum allowed value is 32 (provided: " << fc << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL); - } - - co.iFlightFlagSize = fc; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int bs = cast_optval(optval, optlen); - if (bs <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndBufSize = bs / (co.iMSS - srt::CPacket::UDP_HDR_SIZE); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val <= 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - srt::CPacket::UDP_HDR_SIZE; - - if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) - co.iRcvBufSize = val / mssin_size; - else - co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; - - // recv buffer MUST not be greater than FC size - if (co.iRcvBufSize > co.iFlightFlagSize) - co.iRcvBufSize = co.iFlightFlagSize; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.Linger = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iUDPSndBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iUDPRcvBufSize = std::max(co.iMSS, cast_optval(optval, optlen)); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bRendezvous = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndTimeOut = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvTimeOut = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bSynSending = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bSynRecving = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bReuseAddr = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.llMaxBW = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - int val = cast_optval(optval, optlen); - if (!(val == -1) && !((val >= 1) && (val <= 255))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iIpTTL = cast_optval(optval); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iIpToS = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_BINDTODEVICE - using namespace std; - using namespace srt_logging; - - string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); - if (val.size() >= IFNAMSIZ) - { - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.sBindToDevice = val; -#else - (void)co; // prevent warning - (void)optval; - (void)optlen; - LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.llInputBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int64_t val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.llMinInputBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int32_t val = cast_optval(optval, optlen); - if (val < 5 || val > 100) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iOverheadBW = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bDataSender = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bTSBPD = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvLatency = val; - co.iPeerLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iRcvLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iPeerLatency = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bTLPktDrop = cast_optval(optval, optlen); - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < -1) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iSndDropDelay = val; - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_ENCRYPTION - // Password must be 10-80 characters. - // Or it can be empty to clear the password. - if ((optlen != 0) && (optlen < 10 || optlen > HAICRYPT_SECRET_MAX_SZ)) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memset(&co.CryptoSecret, 0, sizeof(co.CryptoSecret)); - co.CryptoSecret.typ = HAICRYPT_SECTYP_PASSPHRASE; - co.CryptoSecret.len = (optlen <= (int)sizeof(co.CryptoSecret.str) ? optlen : (int)sizeof(co.CryptoSecret.str)); - memcpy((co.CryptoSecret.str), optval, co.CryptoSecret.len); -#else - (void)co; // prevent warning - (void)optval; - if (optlen == 0) - return; // Allow to set empty passphrase if no encryption supported. - - LOGC(aclog.Error, log << "SRTO_PASSPHRASE: encryption not enabled at compile time"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; -#ifdef SRT_ENABLE_ENCRYPTION - const int v = cast_optval(optval, optlen); - int const allowed[4] = { - 0, // Default value, if this results for initiator, defaults to 16. See below. - 16, // AES-128 - 24, // AES-192 - 32 // AES-256 - }; - const int *const allowed_end = allowed + 4; - if (std::find(allowed, allowed_end, v) == allowed_end) - { - LOGC(aclog.Error, - log << "Invalid value for option SRTO_PBKEYLEN: " << v << "; allowed are: 0, 16, 24, 32"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - // Note: This works a little different in HSv4 and HSv5. - - // HSv4: - // The party that is set SRTO_SENDER will send KMREQ, and it will - // use default value 16, if SRTO_PBKEYLEN is the default value 0. - // The responder that receives KMRSP has nothing to say about - // PBKEYLEN anyway and it will take the length of the key from - // the initiator (sender) as a good deal. - // - // HSv5: - // The initiator (independently on the sender) will send KMREQ, - // and as it should be the sender to decide about the PBKEYLEN. - // Your application should do the following then: - // 1. The sender should set PBKEYLEN to the required value. - // 2. If the sender is initiator, it will create the key using - // its preset PBKEYLEN (or default 16, if not set) and the - // receiver-responder will take it as a good deal. - // 3. Leave the PBKEYLEN value on the receiver as default 0. - // 4. If sender is responder, it should then advertise the PBKEYLEN - // value in the initial handshake messages (URQ_INDUCTION if - // listener, and both URQ_WAVEAHAND and URQ_CONCLUSION in case - // of rendezvous, as it is the matter of luck who of them will - // eventually become the initiator). This way the receiver - // being an initiator will set iSndCryptoKeyLen before setting - // up KMREQ for sending to the sender-responder. - // - // Note that in HSv5 if both sides set PBKEYLEN, the responder - // wins, unless the initiator is a sender (the effective PBKEYLEN - // will be the one advertised by the responder). If none sets, - // PBKEYLEN will default to 16. - - co.iSndCryptoKeyLen = v; -#else - (void)co; // prevent warning - (void)optval; - (void)optlen; - LOGC(aclog.Error, log << "SRTO_PBKEYLEN: encryption not enabled at compile time"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); -#endif - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bRcvNakReport = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - using namespace srt::sync; - co.tdConnTimeOut = milliseconds_from(val); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bDriftTracer = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iMaxReorderTolerance = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.uSrtVersion = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.uMinimumPeerSrtVersion = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - if (size_t(optlen) > CSrtConfig::MAX_SID_LENGTH) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.sStreamName.set((const char*)optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - std::string val; - if (optlen == -1) - val = (const char*)optval; - else - val.assign((const char*)optval, optlen); - - // Translate alias - if (val == "vod") - val = "file"; - - bool res = SrtCongestion::exists(val); - if (!res) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.sCongestion.set(val); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bMessageAPI = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - const int val = cast_optval(optval, optlen); - if (val < 0) - { - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (val > SRT_LIVE_MAX_PLSIZE) - { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE, maximum payload per MTU."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - if (!co.sPacketFilterConfig.empty()) - { - // This means that the filter might have been installed before, - // and the fix to the maximum payload size was already applied. - // This needs to be checked now. - srt::SrtFilterConfig fc; - if (!srt::ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) - { - // Break silently. This should not happen - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds SRT_LIVE_MAX_PLSIZE decreased by " << fc.extra_size - << " required for packet filter header"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - co.zExpPayloadSize = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - // XXX Note that here the configuration for SRTT_LIVE - // is the same as DEFAULT VALUES for these fields set - // in CUDT::CUDT. - switch (cast_optval(optval, optlen)) - { - case SRTT_LIVE: - // Default live options: - // - tsbpd: on - // - latency: 120ms - // - linger: off - // - congctl: live - // - extraction method: message (reading call extracts one message) - co.bTSBPD = true; - co.iRcvLatency = SRT_LIVE_DEF_LATENCY_MS; - co.iPeerLatency = 0; - co.bTLPktDrop = true; - co.iSndDropDelay = 0; - co.bMessageAPI = true; - co.bRcvNakReport = true; - co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; - co.Linger.l_onoff = 0; - co.Linger.l_linger = 0; - co.sCongestion.set("live", 4); - break; - - case SRTT_FILE: - // File transfer mode: - // - tsbpd: off - // - latency: 0 - // - linger: on - // - congctl: file (original UDT congestion control) - // - extraction method: stream (reading call extracts as many bytes as available and fits in buffer) - co.bTSBPD = false; - co.iRcvLatency = 0; - co.iPeerLatency = 0; - co.bTLPktDrop = false; - co.iSndDropDelay = -1; - co.bMessageAPI = false; - co.bRcvNakReport = false; - co.zExpPayloadSize = 0; // use maximum - co.Linger.l_onoff = 1; - co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; - co.sCongestion.set("file", 4); - break; - - default: - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } -}; - -#if ENABLE_EXPERIMENTAL_BONDING -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iGroupConnect = cast_optval(optval, optlen); - } -}; -#endif - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - - const int val = cast_optval(optval, optlen); - if (val < 0) - { - LOGC(aclog.Error, - log << "SRTO_KMREFRESHRATE=" << val << " can't be negative"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - // Changing the KMREFRESHRATE sets KMPREANNOUNCE to the maximum allowed value - co.uKmRefreshRatePkt = (unsigned) val; - - if (co.uKmPreAnnouncePkt == 0 && co.uKmRefreshRatePkt == 0) - return; // Both values are default - - const unsigned km_preanno = co.uKmPreAnnouncePkt == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : co.uKmPreAnnouncePkt; - const unsigned km_refresh = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - - if (co.uKmPreAnnouncePkt == 0 || km_preanno > (km_refresh - 1) / 2) - { - co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; - LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" - << std::hex << co.uKmPreAnnouncePkt); - } - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - - const int val = cast_optval(optval, optlen); - if (val < 0) - { - LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=" << val << " can't be negative"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - const unsigned km_preanno = val == 0 ? HAICRYPT_DEF_KM_PRE_ANNOUNCE : val; - const unsigned kmref = co.uKmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : co.uKmRefreshRatePkt; - if (km_preanno > (kmref - 1) / 2) - { - LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) - << " - OPTION REJECTED."); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.uKmPreAnnouncePkt = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.bEnforcedEnc = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - const int val = cast_optval(optval, optlen); - if (val < 0) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - co.iPeerIdleTimeout = val; - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iIpV6Only = cast_optval(optval, optlen); - } -}; - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - std::string arg((const char*)optval, optlen); - // Parse the configuration string prematurely - srt::SrtFilterConfig fc; - srt::PacketFilter::Factory* fax = 0; - if (!srt::ParseFilterConfig(arg, (fc), (&fax))) - { - LOGC(aclog.Error, - log << "SRTO_PACKETFILTER: Incorrect syntax. Use: FILTERTYPE[,KEY:VALUE...]. " - "FILTERTYPE (" - << fc.type << ") must be installed (or builtin)"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - std::string error; - if (!fax->verifyConfig(fc, (error))) - { - LOGC(aclog.Error, log << "SRTO_PACKETFILTER: Incorrect config: " << error); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (co.zExpPayloadSize > efc_max_payload_size) - { - LOGC(aclog.Warn, - log << "Due to filter-required extra " << fc.extra_size << " bytes, SRTO_PAYLOADSIZE fixed to " - << efc_max_payload_size << " bytes"); - co.zExpPayloadSize = efc_max_payload_size; - } - - co.sPacketFilterConfig.set(arg); - } -}; - -#if ENABLE_EXPERIMENTAL_BONDING -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - using namespace srt_logging; - // This option is meaningless for the socket itself. - // It's set here just for the sake of setting it on a listener - // socket so that it is then applied on the group when a - // group connection is configuired. - const int val = cast_optval(optval, optlen); - - // Search if you already have SRTO_PEERIDLETIMEO set - - const int idletmo = co.iPeerIdleTimeout; - - // Both are in milliseconds. - // This option is RECORDED in microseconds, while - // idletmo is recorded in milliseconds, only translated to - // microseconds directly before use. - if (val >= idletmo) - { - LOGC(aclog.Error, log << "group option: SRTO_GROUPSTABTIMEO(" << val - << ") exceeds SRTO_PEERIDLETIMEO(" << idletmo << ")"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - co.uStabilityTimeout = val * 1000; - } -}; -#endif - -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.iRetransmitAlgo = cast_optval(optval, optlen); - } -}; - -#if TEMPLATE -#endif - #endif From 127c85c1098a91864528235342735de54156de59 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 5 Jul 2021 19:30:54 +0800 Subject: [PATCH 111/683] [core] Put CSrtConfigSetter into anonymous namespace And use simple switch case. The library size is reduced from 1224 KB to 1216 KB on macOS build with: -DENABLE_TESTING=ON -DENABLE_UNITTESTS=ON -DENABLE_HEAVY_LOGGING=ON \ -DUSE_ENCLIB=mbedtls -DENABLE_EXPERIMENTAL_BONDING=ON -DENABLE_ENCRYPTION=OFF \ -DENABLE_STDCXX_SYNC=ON --- srtcore/socketconfig.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 8cb06a08f..2fcc475b2 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -54,6 +54,7 @@ written by extern const int32_t SRT_DEF_VERSION = SrtParseVersion(SRT_VERSION); +namespace { typedef void setter_function(CSrtConfig& co, const void* optval, int optlen); template @@ -872,15 +873,11 @@ struct CSrtConfigSetter } }; -static struct SrtConfigSetter +int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int optlen) { - setter_function* fn[SRTO_E_SIZE]; - - SrtConfigSetter() + switch (optName) { - memset(fn, 0, sizeof fn); - -#define DISPATCH(optname) fn[optname] = &CSrtConfigSetter::set; +#define DISPATCH(optname) case optname: CSrtConfigSetter::set(co, optval, optlen); return 0; DISPATCH(SRTO_MSS); DISPATCH(SRTO_FC); @@ -937,17 +934,16 @@ static struct SrtConfigSetter DISPATCH(SRTO_RETRANSMITALGO); #undef DISPATCH + default: + return -1; } -} srt_config_setter; +} + +} // anonymous namespace int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) { - setter_function* fn = srt_config_setter.fn[optName]; - if (!fn) - return -1; // No such option - - fn(*this, optval, optlen); // MAY THROW EXCEPTION. - return 0; + return dispatchSet(optName, *this, optval, optlen); } #if ENABLE_EXPERIMENTAL_BONDING From 877adfa5fc4c98050faaa5062c0ab5097d634ca1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Jul 2021 15:21:35 +0200 Subject: [PATCH 112/683] [docs] Removed unused SRTO_FC from config function in configuration guidelines. --- docs/API/configuration-guidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index 1e6d2111e..54d3cd15a 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -32,7 +32,7 @@ int getRbufSizePkts(int SRTO_RCVBUF, int SRTO_MSS, int SRTO_FC) // UDP header size is assumed to be 28 bytes // 20 bytes IPv4 + 8 bytes of UDP const int UDPHDR_SIZE = 28; - const in pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); + const int pkts = (rbuf_size / (SRTO_MSS - UDPHDR_SIZE)); return min(pkts, SRTO_FC); } @@ -88,7 +88,7 @@ where ```c++ -auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS, int SRTO_FC) +auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS) { const int UDPHDR_SIZE = 28; const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; @@ -99,7 +99,7 @@ auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int m // Configuring -const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS, SRTO_FC); +const auto [fc, rcvbuf] = CalculateTargetRBufSize(msRTT, bpsRate, bytesPayloadSize, SRTO_RCVLATENCY, SRTO_MSS); int optval = fc; int optlen = sizeof optval; From ea4edffc257bb634fe8944e530ed5fc355860c92 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 27 Jul 2021 00:41:16 +0200 Subject: [PATCH 113/683] [core] Moved compiler attribute definitions --- srtcore/srt_attr_defs.h | 80 +++++++++++++++++++++++++++++++++++++---- srtcore/utilities.h | 72 +------------------------------------ 2 files changed, 75 insertions(+), 77 deletions(-) diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 24bc8bc84..82e6e5e3a 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -3,23 +3,91 @@ * Copyright (c) 2019 Haivision Systems Inc. * * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this + * License, v.2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ /***************************************************************************** The file contains various planform and compiler dependent attribute definitions used by SRT library internally. - -1. Attributes for thread safety analysis - - Clang (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). - - Other compilers: none. - *****************************************************************************/ #ifndef INC_SRT_ATTR_DEFS_H #define INC_SRT_ATTR_DEFS_H +// ATTRIBUTES: +// +// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) +// ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) +// ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode +// ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. +// ATR_CONSTEXPR: In C++11: `constexpr`. Otherwise empty. +// ATR_OVERRIDE: In C++11: `override`. Otherwise empty. +// ATR_FINAL: In C++11: `final`. Otherwise empty. + +#ifdef __GNUG__ +#define ATR_DEPRECATED __attribute__((deprecated)) +#else +#define ATR_DEPRECATED +#endif + +#if defined(__cplusplus) && __cplusplus > 199711L +#define HAVE_CXX11 1 +// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, +// however it's only the "most required C++11 support". +#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only! +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#else +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_NOTHROW noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#endif +#elif defined(_MSC_VER) && _MSC_VER >= 1800 +// Microsoft Visual Studio supports C++11, but not fully, +// and still did not change the value of __cplusplus. Treat +// this special way. +// _MSC_VER == 1800 means Microsoft Visual Studio 2013. +#define HAVE_CXX11 1 +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 +#define HAVE_FULL_CXX11 1 +#define ATR_NOEXCEPT noexcept +#define ATR_NOTHROW noexcept +#define ATR_CONSTEXPR constexpr +#define ATR_OVERRIDE override +#define ATR_FINAL final +#else +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#endif +#else +#define HAVE_CXX11 0 +#define ATR_NOEXCEPT +#define ATR_NOTHROW throw() +#define ATR_CONSTEXPR +#define ATR_OVERRIDE +#define ATR_FINAL +#endif // __cplusplus + +#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1 +#error "The currently compiled application required C++11, but your compiler doesn't support it." +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Attributes for thread safety analysis +// - Clang TSA (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader). +// - MSVC SAL (partially). +// - Other compilers: none. +/////////////////////////////////////////////////////////////////////////////// #if _MSC_VER >= 1920 // In case of MSVC these attributes have to preceed the attributed objects (variable, function). // E.g. SRT_ATTR_GUARDED_BY(mtx) int object; diff --git a/srtcore/utilities.h b/srtcore/utilities.h index ed96306f5..6d888b3d8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -16,81 +16,11 @@ written by #ifndef INC_SRT_UTILITIES_H #define INC_SRT_UTILITIES_H -// ATTRIBUTES: -// -// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings) -// ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used) -// ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode -// ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++. -// ATR_CONSTEXPR: In C++11: `constexpr`. Otherwise empty. -// ATR_OVERRIDE: In C++11: `override`. Otherwise empty. -// ATR_FINAL: In C++11: `final`. Otherwise empty. - - -#ifdef __GNUG__ -#define ATR_DEPRECATED __attribute__((deprecated)) -#else -#define ATR_DEPRECATED -#endif -#if defined(__cplusplus) && __cplusplus > 199711L -#define HAVE_CXX11 1 - -// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, -// however it's only the "most required C++11 support". -#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only! -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#else -#define HAVE_FULL_CXX11 1 -#define ATR_NOEXCEPT noexcept -#define ATR_NOTHROW noexcept -#define ATR_CONSTEXPR constexpr -#define ATR_OVERRIDE override -#define ATR_FINAL final -#endif - -// Microsoft Visual Studio supports C++11, but not fully, -// and still did not change the value of __cplusplus. Treat -// this special way. -// _MSC_VER == 1800 means Microsoft Visual Studio 2013. -#elif defined(_MSC_VER) && _MSC_VER >= 1800 -#define HAVE_CXX11 1 -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 -#define HAVE_FULL_CXX11 1 -#define ATR_NOEXCEPT noexcept -#define ATR_NOTHROW noexcept -#define ATR_CONSTEXPR constexpr -#define ATR_OVERRIDE override -#define ATR_FINAL final -#else -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL -#endif -#else -#define HAVE_CXX11 0 -#define ATR_NOEXCEPT -#define ATR_NOTHROW throw() -#define ATR_CONSTEXPR -#define ATR_OVERRIDE -#define ATR_FINAL - -#endif - -#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1 -#error "The currently compiled application required C++11, but your compiler doesn't support it." -#endif - - // Windows warning disabler #define _CRT_SECURE_NO_WARNINGS 1 #include "platform_sys.h" +#include "srt_attr_defs.h" // defines HAVE_CXX11 // Happens that these are defined, undefine them in advance #undef min From 158f35d6ba24d92d5a217f06ef402b133bfd50ae Mon Sep 17 00:00:00 2001 From: Laurent Bigonville Date: Fri, 30 Jul 2021 00:04:24 +0200 Subject: [PATCH 114/683] [core] Fix FTBFS on Debian kfreebsd Fixes: #2066 --- srtcore/epoll.cpp | 4 ++++ srtcore/utilities.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 5755c9dff..bfce672cc 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -58,6 +58,10 @@ modified by #include #include +#if defined(__FreeBSD_kernel__) +#include +#endif + #include "common.h" #include "epoll.h" #include "logging.h" diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 6d888b3d8..b1f21fbb2 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -115,7 +115,7 @@ written by # include -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) # include From 5ac27a3733c7938eab5fc147d10412b8304fafbd Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 2 Aug 2021 17:15:04 +0200 Subject: [PATCH 115/683] [tests] Tests for socket options API (#1929) --- docs/API/API-socket-options.md | 2 + test/any.hpp | 504 +++++++++++++++++++++++++++ test/filelist.maf | 2 + test/test_listen_callback.cpp | 6 +- test/test_socket_options.cpp | 598 ++++++++++++++++++++++++++++++++- 5 files changed, 1107 insertions(+), 5 deletions(-) create mode 100644 test/any.hpp diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 3c9f91556..b87d80ab0 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -847,6 +847,8 @@ dedicated network settings. MSS is not to be confused with the size of the UDP payload or SRT payload - this size is the size of the IP packet, including the UDP and SRT headers* +THe value of `SRTO_MSS` must not exceed `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. + [Return to list](#list-of-options) --- diff --git a/test/any.hpp b/test/any.hpp new file mode 100644 index 000000000..d47722bae --- /dev/null +++ b/test/any.hpp @@ -0,0 +1,504 @@ +// +// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. +// +// See also: +// + http://en.cppreference.com/w/cpp/any +// + http://en.cppreference.com/w/cpp/experimental/any +// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any +// + https://cplusplus.github.io/LWG/lwg-active.html#2509 +// +// +// Copyright (c) 2016 Denilson das Mercês Amorim +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef LINB_ANY_HPP +#define LINB_ANY_HPP +#pragma once +#include +#include +#include + + +#if defined(PARTICLE) +#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS) +# define ANY_IMPL_NO_EXCEPTIONS +# endif +#else +// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, +// but you must ensure not to cast badly when passing an `any' object to any_cast(any) +#endif + +#if defined(PARTICLE) +#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI) +# define ANY_IMPL_NO_RTTI +# endif +#else +// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, +// in order to disable functions working with the typeid of a type +#endif + + +namespace linb +{ + +class bad_any_cast : public std::bad_cast +{ +public: + const char* what() const noexcept override + { + return "bad any cast"; + } +}; + +class any final +{ +public: + /// Constructs an object of type any with an empty state. + any() : + vtable(nullptr) + { + } + + /// Constructs an object of type any with an equivalent state as other. + any(const any& rhs) : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->copy(rhs.storage, this->storage); + } + } + + /// Constructs an object of type any with a state equivalent to the original state of other. + /// rhs is left in a valid but otherwise unspecified state. + any(any&& rhs) noexcept : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->move(rhs.storage, this->storage); + rhs.vtable = nullptr; + } + } + + /// Same effect as this->clear(). + ~any() + { + this->clear(); + } + + /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + this->construct(std::forward(value)); + } + + /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. + any& operator=(const any& rhs) + { + any(rhs).swap(*this); + return *this; + } + + /// Has the same effect as any(std::move(rhs)).swap(*this). + /// + /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid + /// but otherwise unspecified state. + any& operator=(any&& rhs) noexcept + { + any(std::move(rhs)).swap(*this); + return *this; + } + + /// Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any& operator=(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + any(std::forward(value)).swap(*this); + return *this; + } + + /// If not empty, destroys the contained object. + void clear() noexcept + { + if(!empty()) + { + this->vtable->destroy(storage); + this->vtable = nullptr; + } + } + + /// Returns true if *this has no contained object, otherwise false. + bool empty() const noexcept + { + return this->vtable == nullptr; + } + +#ifndef ANY_IMPL_NO_RTTI + /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). + const std::type_info& type() const noexcept + { + return empty()? typeid(void) : this->vtable->type(); + } +#endif + + /// Exchange the states of *this and rhs. + void swap(any& rhs) noexcept + { + if(this->vtable != rhs.vtable) + { + any tmp(std::move(rhs)); + + // move from *this to rhs. + rhs.vtable = this->vtable; + if(this->vtable != nullptr) + { + this->vtable->move(this->storage, rhs.storage); + //this->vtable = nullptr; -- unneeded, see below + } + + // move from tmp (previously rhs) to *this. + this->vtable = tmp.vtable; + if(tmp.vtable != nullptr) + { + tmp.vtable->move(tmp.storage, this->storage); + tmp.vtable = nullptr; + } + } + else // same types + { + if(this->vtable != nullptr) + this->vtable->swap(this->storage, rhs.storage); + } + } + +private: // Storage and Virtual Method Table + union storage_union + { + using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; + + void* dynamic; + stack_storage_t stack; // 2 words for e.g. shared_ptr + }; + + /// Base VTable specification. + struct vtable_type + { + // Note: The caller is responssible for doing .vtable = nullptr after destructful operations + // such as destroy() and/or move(). + +#ifndef ANY_IMPL_NO_RTTI + /// The type of the object this vtable is for. + const std::type_info& (*type)() noexcept; +#endif + + /// Destroys the object in the union. + /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. + void(*destroy)(storage_union&) noexcept; + + /// Copies the **inner** content of the src union into the yet unitialized dest union. + /// As such, both inner objects will have the same state, but on separate memory locations. + void(*copy)(const storage_union& src, storage_union& dest); + + /// Moves the storage from src to the yet unitialized dest union. + /// The state of src after this call is unspecified, caller must ensure not to use src anymore. + void(*move)(storage_union& src, storage_union& dest) noexcept; + + /// Exchanges the storage between lhs and rhs. + void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; + }; + + /// VTable for dynamically allocated storage. + template + struct vtable_dynamic + { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info& type() noexcept + { + return typeid(T); + } +#endif + + static void destroy(storage_union& storage) noexcept + { + //assert(reinterpret_cast(storage.dynamic)); + delete reinterpret_cast(storage.dynamic); + } + + static void copy(const storage_union& src, storage_union& dest) + { + dest.dynamic = new T(*reinterpret_cast(src.dynamic)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + dest.dynamic = src.dynamic; + src.dynamic = nullptr; + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + // just exchage the storage pointers. + std::swap(lhs.dynamic, rhs.dynamic); + } + }; + + /// VTable for stack allocated storage. + template + struct vtable_stack + { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info& type() noexcept + { + return typeid(T); + } +#endif + + static void destroy(storage_union& storage) noexcept + { + reinterpret_cast(&storage.stack)->~T(); + } + + static void copy(const storage_union& src, storage_union& dest) + { + new (&dest.stack) T(reinterpret_cast(src.stack)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + // one of the conditions for using vtable_stack is a nothrow move constructor, + // so this move constructor will never throw a exception. + new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); + destroy(src); + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + storage_union tmp_storage; + move(rhs, tmp_storage); + move(lhs, rhs); + move(tmp_storage, lhs); + } + }; + + /// Whether the type T must be dynamically allocated or can be stored on the stack. + template + struct requires_allocation : + std::integral_constant::value // N4562 §6.3/3 [any.class] + && sizeof(T) <= sizeof(storage_union::stack) + && std::alignment_of::value <= std::alignment_of::value)> + {}; + + /// Returns the pointer to the vtable of the type T. + template + static vtable_type* vtable_for_type() + { + using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; + static vtable_type table = { +#ifndef ANY_IMPL_NO_RTTI + VTableType::type, +#endif + VTableType::destroy, + VTableType::copy, VTableType::move, + VTableType::swap, + }; + return &table; + } + +protected: + template + friend const T* any_cast(const any* operand) noexcept; + template + friend T* any_cast(any* operand) noexcept; + +#ifndef ANY_IMPL_NO_RTTI + /// Same effect as is_same(this->type(), t); + bool is_typed(const std::type_info& t) const + { + return is_same(this->type(), t); + } +#endif + +#ifndef ANY_IMPL_NO_RTTI + /// Checks if two type infos are the same. + /// + /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the + /// type infos, otherwise does an actual comparision. Checking addresses is + /// only a valid approach when there's no interaction with outside sources + /// (other shared libraries and such). + static bool is_same(const std::type_info& a, const std::type_info& b) + { +#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE + return &a == &b; +#else + return a == b; +#endif + } +#endif + + /// Casts (with no type_info checks) the storage pointer as const T*. + template + const T* cast() const noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + + /// Casts (with no type_info checks) the storage pointer as T*. + template + T* cast() noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + +private: + storage_union storage; // on offset(0) so no padding for align + vtable_type* vtable; + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + storage.dynamic = new T(std::forward(value)); + } + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + new (&storage.stack) T(std::forward(value)); + } + + /// Chooses between stack and dynamic allocation for the type decay_t, + /// assigns the correct vtable, and constructs the object on our storage. + template + void construct(ValueType&& value) + { + using T = typename std::decay::type; + + this->vtable = vtable_for_type(); + + do_construct(std::forward(value)); + } +}; + + + +namespace detail +{ + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) + { + return std::move(*p); + } + + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) + { + return *p; + } +} + +/// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(const any& operand) +{ + auto p = any_cast::type>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return *p; +} + +/// Performs *any_cast>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(any& operand) +{ + auto p = any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return *p; +} + +/// +/// If ValueType is MoveConstructible and isn't a lvalue reference, performs +/// std::move(*any_cast>(&operand)), otherwise +/// *any_cast>(&operand). Throws bad_any_cast on failure. +/// +template +inline ValueType any_cast(any&& operand) +{ + using can_move = std::integral_constant::value + && !std::is_lvalue_reference::value>; + + auto p = any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if(p == nullptr) throw bad_any_cast(); +#endif + return detail::any_cast_move_if_true(p, can_move()); +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline const ValueType* any_cast(const any* operand) noexcept +{ + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline ValueType* any_cast(any* operand) noexcept +{ + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; +} + +} + +namespace std +{ + inline void swap(linb::any& lhs, linb::any& rhs) noexcept + { + lhs.swap(rhs); + } +} + +#endif diff --git a/test/filelist.maf b/test/filelist.maf index d92cabd9d..0cb25cee8 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -1,3 +1,5 @@ +HEADERS +any.hpp SOURCES test_buffer.cpp diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index c8a441273..7abe6916a 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -183,8 +183,12 @@ int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion #if SRT_ENABLE_ENCRYPTION cerr << "TEST: Setting password '" << exp_pw << "' as per user '" << username << "'\n"; - srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()); + EXPECT_EQ(srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()), SRT_SUCCESS); #endif + + // Checking that SRTO_RCVLATENCY (PRE option) can be altered in the listener callback. + int optval = 200; + EXPECT_EQ(srt_setsockflag(ns, SRTO_RCVLATENCY, &optval, sizeof optval), SRT_SUCCESS); return 0; } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index d2e1da704..47700c9a6 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -13,9 +13,11 @@ #include #include #include +#include // SRT includes -#include +#include "any.hpp" +#include "socketconfig.h" #include "srt.h" using namespace std; @@ -36,15 +38,26 @@ class TestSocketOptions } public: - void StartListener() + void BindListener() { // Specify address of the listener sockaddr* psa = (sockaddr*)&m_sa; ASSERT_NE(srt_bind(m_listen_sock, psa, sizeof m_sa), SRT_ERROR); + } + + void StartListener() + { + BindListener(); srt_listen(m_listen_sock, 1); } + int Connect() + { + sockaddr* psa = (sockaddr*)&m_sa; + return srt_connect(m_caller_sock, psa, sizeof m_sa); + } + SRTSOCKET EstablishConnection() { auto accept_async = [](SRTSOCKET listen_sock) { @@ -55,8 +68,7 @@ class TestSocketOptions }; auto accept_res = async(launch::async, accept_async, m_listen_sock); - sockaddr* psa = (sockaddr*)&m_sa; - const int connect_res = srt_connect(m_caller_sock, psa, sizeof m_sa); + const int connect_res = Connect(); EXPECT_EQ(connect_res, SRT_SUCCESS); const SRTSOCKET accepted_sock = accept_res.get(); @@ -108,6 +120,584 @@ class TestSocketOptions }; +enum class RestrictionType +{ + PREBIND = 0, + PRE = 1, + POST = 2 +}; + +const char* RestrictionTypeStr(RestrictionType val) +{ + switch (val) + { + case RestrictionType::PREBIND: + return "PREBIND"; + break; + case RestrictionType::PRE: + return "PRE"; + break; + case RestrictionType::POST: + return "POST"; + break; + default: + break; + } + return "INVALID"; +} + +struct OptionTestEntry +{ + SRT_SOCKOPT optid; + const char* optname; // TODO: move to a separate array, function or std::map. + RestrictionType restriction; // TODO: consider using SRTO_R_PREBIND, etc. from core.cpp + size_t opt_len; + linb::any min_val; + linb::any max_val; + linb::any dflt_val; + linb::any ndflt_val; + vector invalid_vals; +}; + +static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. +static const size_t DFT_MTU_SIZE = 1500; // Default MTU size +static const size_t SRT_PKT_SIZE = DFT_MTU_SIZE - UDP_HDR_SIZE; // MTU without UDP header + +const OptionTestEntry g_test_matrix_options[] = +{ + // Option ID, Option Name | Restriction | optlen | min | max | default | nondefault | invalid vals | + //SRTO_BINDTODEVICE + //{ SRTO_CONGESTION, "SRTO_CONGESTION", RestrictionType::PRE, 4, "live", "file", "live", "file", {"liv", ""} }, + { SRTO_CONNTIMEO, "SRTO_CONNTIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 3000, 250, {-1} }, + { SRTO_DRIFTTRACER, "SRTO_DRIFTTRACER", RestrictionType::POST, sizeof(bool), false, true, true, false, {} }, + { SRTO_ENFORCEDENCRYPTION, "SRTO_ENFORCEDENCRYPTION", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + //SRTO_EVENT + { SRTO_FC, "SRTO_FC", RestrictionType::PRE, sizeof(int), 32, INT32_MAX, 25600, 10000, {-1, 31} }, + //SRTO_GROUPCONNECT + //SRTO_GROUPSTABTIMEO + //SRTO_GROUPTYPE + //SRTO_INPUTBW + //SRTO_IPTOS + //SRTO_IPTTL + //SRTO_IPV6ONLY + //SRTO_ISN + { SRTO_KMPREANNOUNCE, "SRTO_KMPREANNOUNCE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} }, + { SRTO_KMREFRESHRATE, "SRTO_KMREFRESHRATE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} }, + //SRTO_KMSTATE + { SRTO_LATENCY, "SRTO_LATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 200, {-1} }, + //SRTO_LINGER + { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, + //SRTO_MAXBW + { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + //SRTO_MININPUTBW + { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, + { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, + { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101} }, + //SRTO_PACKETFILTER + //SRTO_PASSPHRASE + { SRTO_PAYLOADSIZE, "SRTO_PAYLOADSIZE", RestrictionType::PRE, sizeof(int), 0, 1456, 1316, 1400, {-1, 1500} }, + //SRTO_PBKEYLEN + //SRTO_PEERIDLETIMEO + { SRTO_PEERIDLETIMEO, "SRTO_PEERIDLETIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 5000, 4500, {-1} }, + { SRTO_PEERLATENCY, "SRTO_PEERLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 180, {-1} }, + //SRTO_PEERVERSION + { SRTO_RCVBUF, "SRTO_RCVBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, + //SRTO_RCVDATA + //SRTO_RCVKMSTATE + { SRTO_RCVLATENCY, "SRTO_RCVLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 1100, {-1} }, + //SRTO_RCVSYN + { SRTO_RCVTIMEO, "SRTO_RCVTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 2000, {-2} }, + //SRTO_RENDEZVOUS + //SRTO_RETRANSMITALGO + //SRTO_REUSEADDR + //SRTO_SENDER + { SRTO_SNDBUF, "SRTO_SNDBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, + //SRTO_SNDDATA + { SRTO_SNDDROPDELAY, "SRTO_SNDDROPDELAY", RestrictionType::POST, sizeof(int), -1, INT32_MAX, 0, 1500, {-2} }, + //SRTO_SNDKMSTATE + //SRTO_SNDSYN + { SRTO_SNDTIMEO, "SRTO_SNDTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 1400, {-2} }, + //SRTO_STATE + //SRTO_STREAMID + { SRTO_TLPKTDROP, "SRTO_TLPKTDROP", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, + //SRTO_TRANSTYPE + //SRTO_TSBPDMODE + //SRTO_UDP_RCVBUF + //SRTO_UDP_SNDBUF + //SRTO_VERSION +}; + + +template +void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) +{ + ValueType opt_val; + int opt_len = 0; + EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) + << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); + + EXPECT_EQ(opt_val, value) << desc << ": Wrong " << entry.optname << " value " << opt_val; + EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; +} + +typedef char const* strptr; +template<> +void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const strptr& value, const char* desc) +{ + char opt_val[16]; + int opt_len = 0; + EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) + << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); + + EXPECT_EQ(strncmp(opt_val, value, min(opt_len, entry.opt_len)), 0) << desc << ": Wrong " << entry.optname << " value " << opt_val; + EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; +} + +template +void CheckSetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, int expect_return, const char* desc) +{ + ValueType opt_val = value; + int opt_len = entry.opt_len; + EXPECT_EQ(srt_setsockopt(sock, 0, entry.optid, &opt_val, opt_len), expect_return) + << "Setting " << entry.optname << " to " << opt_val << " must " << (expect_return == SRT_SUCCESS ? "succeed" : "fail"); + + if (expect_return == SRT_SUCCESS) + { + CheckGetSockOpt(entry, sock, value, desc); + } + // TODO: else check the previous value is in force +} + +template +bool CheckDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType dflt_val = linb::any_cast(entry.dflt_val); + CheckGetSockOpt(entry, sock, dflt_val, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " default value type: " << entry.dflt_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckSetNonDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, int expected_return, const char* desc) +{ + try { + /*const ValueType dflt_val = linb::any_cast(entry.dflt_val); + const ValueType min_val = linb::any_cast(entry.min_val); + const ValueType max_val = linb::any_cast(entry.max_val);*/ + //const ValueType ndflt_val = (min_val != dflt_val) ? min_val : max_val; + + const ValueType ndflt_val = linb::any_cast(entry.ndflt_val);; + + CheckSetSockOpt(entry, sock, ndflt_val, expected_return, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " non-default value type: " << entry.ndflt_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckMinValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType min_val = linb::any_cast(entry.min_val); + CheckSetSockOpt(entry, sock, min_val, SRT_SUCCESS, desc); + + const ValueType dflt_val = linb::any_cast(entry.dflt_val); + CheckSetSockOpt(entry, sock, dflt_val, SRT_SUCCESS, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " min value type: " << entry.min_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckMaxValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc) +{ + try { + const ValueType max_val = linb::any_cast(entry.max_val); + CheckSetSockOpt(entry, sock, max_val, SRT_SUCCESS, desc); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " max value type: " << entry.max_val.type().name() << "\n"; + return false; + } + + return true; +} + +template +bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char* sock_name) +{ + for (const auto inval : entry.invalid_vals) + { + try { + const ValueType val = linb::any_cast(inval); + CheckSetSockOpt(entry, sock, val, SRT_ERROR, "[Caller, invalid val]"); + } + catch (const linb::bad_any_cast&) + { + std::cerr << entry.optname << " value type: " << inval.type().name() << "\n"; + return false; + } + } + + return true; +} + +TEST_F(TestSocketOptions, DefaultVals) +{ + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, default]"; + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else if (entry.dflt_val.type() == typeid(const char*)) + { + EXPECT_TRUE(CheckDefaultValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << entry.optname << ": Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +TEST_F(TestSocketOptions, MaxVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + if (entry.optid == SRTO_KMPREANNOUNCE || entry.optid == SRTO_KMREFRESHRATE) + { + cerr << "Skipping " << entry.optname << "\n"; + continue; + } + + const char* test_desc = "[Caller, max value]"; + if (entry.max_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else if (entry.max_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else if (entry.max_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckMaxValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << "Unexpected type " << entry.max_val.type().name(); + } + + // TODO: back to default ? + } +} + +TEST_F(TestSocketOptions, MinVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, min val]"; + if (entry.min_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else if (entry.min_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else if (entry.min_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckMinValue(entry, m_caller_sock, test_desc)); + } + else + { + FAIL() << entry.optname << ": Unexpected type " << entry.min_val.type().name(); + } + + // TODO: back to default + } +} + +TEST_F(TestSocketOptions, InvalidVals) +{ + // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation + for (const auto& entry : g_test_matrix_options) + { + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + + // TODO: expect default is still in force? + } +} + + + +// TODO: taken from test_enforced_encryption +static const char* const socket_state_array[] = { + "IGNORE_SRTS", + "SRTS_INVALID", + "SRTS_INIT", + "SRTS_OPENED", + "SRTS_LISTENING", + "SRTS_CONNECTING", + "SRTS_CONNECTED", + "SRTS_BROKEN", + "SRTS_CLOSING", + "SRTS_CLOSED", + "SRTS_NONEXIST" +}; + +// A trick that allows the array to be indexed by -1 +const char* const* g_socket_state = socket_state_array + 1; + +#if 0 +// No socket option can be set in blocking mode because m_ConnectionLock is required by both srt_setsockopt and srt_connect +// TODO: Use non-blocking mode +TEST_F(TestSocketOptions, RestrictionCallerConnecting) +{ + // The default SRTO_CONNTIMEO is 3 seconds. It is assumed all socket options could be checked. + auto connect_async = [this]() { + return Connect(); + }; + auto connect_res = async(launch::async, connect_async); + + for (int i = 0; i < 100; ++i) + { + if (srt_getsockstate(m_caller_sock) == SRTS_CONNECTING) + break; + + this_thread::sleep_for(chrono::microseconds(100)); + } + + cout << "Running test\n"; + + for (const auto& entry : g_test_matrix_options) + { + if (entry.restriction != RestrictionType::PRE) + continue; + + // Setting a valid minimum value + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, entry.optid, &entry.min_val, entry.opt_len), SRT_ERROR) + << "Setting " << entry.optname << " (PRE) must not succeed while connecting. Sock state: " << g_socket_state[srt_getsockstate(m_caller_sock)]; + } + + connect_res.get(); +} +#endif + +TEST_F(TestSocketOptions, RestrictionBind) +{ + BindListener(); + + for (const auto& entry : g_test_matrix_options) + { + const char* test_desc = "[Caller, after bind]"; + const int expected_res = (entry.restriction == RestrictionType::PREBIND) ? SRT_ERROR : SRT_SUCCESS; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +// Check that only socket option with POST binding can be set on a listener socket in "listening" state. +TEST_F(TestSocketOptions, RestrictionListening) +{ + StartListener(); + + for (const auto& entry : g_test_matrix_options) + { + const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS; + + // Setting a valid minimum value + const char* test_desc ="[Listener, listening]"; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, m_listen_sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } +} + +// Check that only socket option with POST binding can be set on a connected socket (caller and accepted). +TEST_F(TestSocketOptions, RestrictionConnected) +{ + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + for (const auto& entry : g_test_matrix_options) + { + const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS; + + // Setting a valid minimum value + for (SRTSOCKET sock : { m_caller_sock, accepted_sock }) + { + const char* test_desc = sock == m_caller_sock ? "[Caller, connected]" : "[Accepted, connected]"; + + if (entry.dflt_val.type() == typeid(bool)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else if (entry.dflt_val.type() == typeid(int)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else if (entry.dflt_val.type() == typeid(int64_t)) + { + EXPECT_TRUE(CheckSetNonDefaultValue(entry, sock, expected_res, test_desc)) + << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)]; + } + else + { + FAIL() << "Unexpected type " << entry.dflt_val.type().name(); + } + } + } +} + +// TODO: TEST_F(TestSocketOptions, CheckInheritedAfterConnection) +// Check that accepted socket has correct socket option values. +// Check setting and getting SRT_MININPUTBW +TEST_F(TestSocketOptions, TLPktDropInherits) +{ + const bool tlpktdrop_dflt = true; + const bool tlpktdrop_new = false; + + bool optval = tlpktdrop_dflt; + int optlen = (int)(sizeof optval); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &tlpktdrop_new, sizeof tlpktdrop_new), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, tlpktdrop_new); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check accepted socket inherits values + for (SRTSOCKET sock : { m_listen_sock, accepted_sock }) + { + optval = tlpktdrop_dflt; + optlen = (int)(sizeof optval); + EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optlen, (int)(sizeof optval)); + EXPECT_EQ(optval, tlpktdrop_new); + } + + this_thread::sleep_for(chrono::seconds(2)); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + +TEST_F(TestSocketOptions, Latency) +{ + const int latency_a = 140; + const int latency_b = 100; + const int latency_dflt = 120; + + int optval; + int optlen = (int)(sizeof optval); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &latency_a, sizeof latency_a), SRT_SUCCESS); + EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &latency_b, sizeof latency_b), SRT_SUCCESS); + + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_b); + + StartListener(); + const SRTSOCKET accepted_sock = EstablishConnection(); + + // Check caller socket + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_dflt); + EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_a); + EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(optval, latency_dflt); + + ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); +} + /// A regression test for issue #735, fixed by PR #843. /// Checks propagation of listener's socket option SRTO_LOSSMAXTTL /// on SRT sockets being accepted. From 8f169a9445a52f27f074cbc7b5f2bf92dd24e8e2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 3 Aug 2021 12:19:13 +0200 Subject: [PATCH 116/683] [core] Changed the default SRTO_RETRANSMITALGO to 1 (#2069) SRTO_TRANSTYPE defaults are updated accordingly. --- docs/API/API-socket-options.md | 8 +++---- docs/API/API.md | 38 ++++++++++++++++++++-------------- srtcore/socketconfig.cpp | 8 ++++++- srtcore/socketconfig.h | 2 +- test/test_socket_options.cpp | 2 +- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index b87d80ab0..84606135a 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -239,7 +239,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | | [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | | [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | | [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | | [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | | [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | @@ -1275,12 +1275,12 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | -| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 0 | [0, 1] | RW | GSD | +| `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | Retransmission algorithm to use (SENDER option): -- 0 - Default (retransmit on every loss report). -- 1 - Reduced retransmissions (not more often than once per RTT); reduced +- 0 - Retransmit on every loss report (higher overhead, but slightly higher chance to recover a lost packet). +- 1 - Reduced retransmissions (retransmit not more often than once per RTT); reduced bandwidth consumption. This option is effective only on the sending side. It influences the decision diff --git a/docs/API/API.md b/docs/API/API.md index 594f9a12d..faca780c7 100644 --- a/docs/API/API.md +++ b/docs/API/API.md @@ -521,14 +521,15 @@ either `FASTREXMIT` or `LATEREXMIT`. This will be explained below. Setting `SRTO_TRANSTYPE` to `SRTT_LIVE` sets the following [socket options](API-socket-options.md): -- `SRTO_TSBPDMODE` = true -- `SRTO_RCVLATENCY` = 120 -- `SRTO_PEERLATENCY` = 0 -- `SRTO_TLPKTDROP` = true -- `SRTO_MESSAGEAPI` = true -- `SRTO_NAKREPORT` = true -- `SRTO_PAYLOADSIZE` = 1316 -- `SRTO_CONGESTION` = "live" +- [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = true +- [`SRTO_RCVLATENCY`](API-socket-options.md#SRTO_RCVLATENCY) = 120 +- [`SRTO_PEERLATENCY`](API-socket-options.md#SRTO_PEERLATENCY) = 0 +- [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) = true +- [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = true +- [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT) = true +- [`SRTO_RETRANSMITALGO`](API-socket-options.md#SRTO_RETRANSMITALGO) = 1 +- [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) = 1316 +- [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION) = "live" In this mode, every call to a sending function is allowed to send only so much data, as declared by `SRTO_PAYLOADSIZE`, whose value is still @@ -580,6 +581,10 @@ loss report itself was lost. Without it, the loss report will be always reported just once and never repeated again, and then the lost payload packet will be probably dropped by the TLPKTDROP mechanism. +- `SRTO_RETRANSMITALGO`: Given the receiver sends periodic NAK reports, +the sender can reduce the retransmission overhead by not retransmitting a loss +more often than once per RTT (value 1). + - `SRTO_PAYLOADSIZE`: Default value is for MPEG TS. If you are going to use SRT to send any different kind of payload, such as, for example, wrapping a live stream in very small frames, then you can use a bigger @@ -598,14 +603,15 @@ NAKREPORT method is considered so effective that FASTREXMIT isn't necessary. Setting `SRTO_TRANSTYPE` to `SRTT_FILE` sets the following [socket options](API-socket-options.md): -- `SRTO_TSBPDMODE` = false -- `SRTO_RCVLATENCY` = 0 -- `SRTO_PEERLATENCY` = 0 -- `SRTO_TLPKTDROP` = false -- `SRTO_MESSAGEAPI` = false -- `SRTO_NAKREPORT` = false -- `SRTO_PAYLOADSIZE` = 0 -- `SRTO_CONGESTION` = "file" +- [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) = false +- [`SRTO_RCVLATENCY`](API-socket-options.md#SRTO_RCVLATENCY) = 0 +- [`SRTO_PEERLATENCY`](API-socket-options.md#SRTO_PEERLATENCY) = 0 +- [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP) = false +- [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) = false +- [`SRTO_NAKREPORT`](API-socket-options.md#SRTO_NAKREPORT) = false +- [`SRTO_RETRANSMITALGO`](API-socket-options.md#SRTO_RETRANSMITALGO) = 0 +- [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) = 0 +- [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION) = "file" In this mode, calling a sending function is allowed to potentially send virtually any size of data. The sending function will HANGUP only if the diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 2fcc475b2..a62881cd7 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -656,6 +656,7 @@ struct CSrtConfigSetter co.iSndDropDelay = 0; co.bMessageAPI = true; co.bRcvNakReport = true; + co.iRetransmitAlgo = 1; co.zExpPayloadSize = SRT_LIVE_DEF_PLSIZE; co.Linger.l_onoff = 0; co.Linger.l_linger = 0; @@ -676,6 +677,7 @@ struct CSrtConfigSetter co.iSndDropDelay = -1; co.bMessageAPI = false; co.bRcvNakReport = false; + co.iRetransmitAlgo = 0; co.zExpPayloadSize = 0; // use maximum co.Linger.l_onoff = 1; co.Linger.l_linger = CSrtConfig::DEF_LINGER_S; @@ -869,7 +871,11 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { - co.iRetransmitAlgo = cast_optval(optval, optlen); + const int val = cast_optval(optval, optlen); + if (val < 0 || val > 1) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + co.iRetransmitAlgo = val; } }; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index a9d2a0856..3e41077d3 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -271,7 +271,7 @@ struct CSrtConfig: CSrtMuxerConfig , iGroupConnect(0) , iPeerIdleTimeout(COMM_RESPONSE_TIMEOUT_MS) , uStabilityTimeout(COMM_DEF_STABILITY_TIMEOUT_US) - , iRetransmitAlgo(0) + , iRetransmitAlgo(1) , llInputBW(0) , llMinInputBW(0) , iOverheadBW(25) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 47700c9a6..bf37513ce 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -209,7 +209,7 @@ const OptionTestEntry g_test_matrix_options[] = //SRTO_RCVSYN { SRTO_RCVTIMEO, "SRTO_RCVTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 2000, {-2} }, //SRTO_RENDEZVOUS - //SRTO_RETRANSMITALGO + { SRTO_RETRANSMITALGO, "SRTO_RETRANSMITALGO", RestrictionType::PRE, sizeof(int), 0, 1, 1, 0, {-1, 2} }, //SRTO_REUSEADDR //SRTO_SENDER { SRTO_SNDBUF, "SRTO_SNDBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} }, From 1a85c029af0c89c7d37f9cf71731bd551db3ec5c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 3 Aug 2021 12:39:58 +0200 Subject: [PATCH 117/683] [core] Annotating CUDT::m_pCryptoControl locking behavior (#2070) --- srtcore/core.cpp | 11 ++++------- srtcore/core.h | 22 ++++++++++++++++------ srtcore/sync.h | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 75c0171ae..cf01d9e35 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1497,14 +1497,11 @@ bool srt::CUDT::createSrtHandshake( w_hs.m_iType |= CHandShake::HS_EXT_HSREQ; bool have_sid = false; - if (srths_cmd == SRT_CMD_HSREQ) + if (srths_cmd == SRT_CMD_HSREQ && !m_config.sStreamName.empty()) { - if (!m_config.sStreamName.empty()) - { - have_sid = true; - w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; - logext << ",SID"; - } + have_sid = true; + w_hs.m_iType |= CHandShake::HS_EXT_CONFIG; + logext << ",SID"; } // If this is a response, we have also information diff --git a/srtcore/core.h b/srtcore/core.h index d190780a0..9a1ad01f0 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -302,6 +302,8 @@ class CUDT static std::vector existingSockets(); void addressAndSend(CPacket& pkt); + + SRT_ATTR_REQUIRES(m_ConnectionLock) void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, size_t srtlen_in = 0); bool isOPT_TsbPd() const { return m_config.bTSBPD; } @@ -457,7 +459,8 @@ class CUDT /// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE) /// @retval -1 Connection failed - SRT_ATR_NODISCARD EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout) ATR_NOEXCEPT; // This function works in case of HSv5 rendezvous. It changes the state // according to the present state and received message type, as well as the @@ -476,9 +479,15 @@ class CUDT /// @param response incoming handshake response packet to be interpreted /// @param serv_addr incoming packet's address /// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN) - SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); - SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); - SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); + + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; + SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket* response, const sockaddr_any& serv_addr); @@ -708,7 +717,8 @@ class CUDT int m_iTsbPdDelay_ms; // Rx delay to absorb burst, in milliseconds int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst, in milliseconds bool m_bTLPktDrop; // Enable Too-late Packet Drop - UniquePtr m_pCryptoControl; // Congestion control SRT class (small data extension) + SRT_ATTR_PT_GUARDED_BY(m_ConnectionLock) + UniquePtr m_pCryptoControl; // Crypto control module CCache* m_pCache; // Network information cache // Congestion control @@ -932,7 +942,7 @@ class CUDT // Failure to create the crypter means that an encrypted // connection should be rejected if ENFORCEDENCRYPTION is on. - SRT_ATR_NODISCARD + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool createCrypter(HandshakeSide side, bool bidi); private: // Generation and processing of packets diff --git a/srtcore/sync.h b/srtcore/sync.h index 081cb41e4..d78aed980 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -446,7 +446,7 @@ class SRT_ATTR_SCOPED_CAPABILITY UniqueLock inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); } -inline bool tryEnterCS(Mutex& m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } +inline bool tryEnterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); } inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); } From 73cad8dab15035d5f320a7e8869d7986649cbc8e Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Mon, 29 Mar 2021 20:07:48 +0800 Subject: [PATCH 118/683] [core] Refactor ThreadName implementation * Add support for macOS and iOS * Add test_threadname --- srtcore/threadname.h | 182 ++++++++++++++++++++++++++++----------- test/filelist.maf | 1 + test/test_threadname.cpp | 61 +++++++++++++ 3 files changed, 192 insertions(+), 52 deletions(-) create mode 100644 test/test_threadname.cpp diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 38fea471b..6fd901aff 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -1,11 +1,11 @@ /* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. - * + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * + * */ /***************************************************************************** @@ -16,85 +16,163 @@ written by #ifndef INC_SRT_THREADNAME_H #define INC_SRT_THREADNAME_H -#ifdef __linux__ - +#if defined(__APPLE__) || defined(__linux__) +#if defined(__linux__) #include +#endif + +#include +#endif + #include +#include +#include + +#include "sync.h" class ThreadName { - char old_name[128]; - char new_name[128]; - bool good; -public: - static const size_t BUFSIZE = 128; +#if defined(__APPLE__) || defined(__linux__) - static bool get(char* namebuf) + class ThreadNameImpl { - return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; - } + public: + static const size_t BUFSIZE = 64; + static const bool DUMMY_IMPL = false; - static bool set(const char* name) - { - return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; - } + static bool get(char* namebuf) + { +#if defined(__linux__) + // since Linux 2.6.11. The buffer should allow space for up to 16 + // bytes; the returned string will be null-terminated. + return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; +#elif defined(__APPLE__) + // since macos(10.6), ios(3.2) + return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0; +#else +#error "unsupported platform" +#endif + } + static bool set(const char* name) + { +#if defined(__linux__) + // The name can be up to 16 bytes long, including the terminating + // null byte. (If the length of the string, including the terminating + // null byte, exceeds 16 bytes, the string is silently truncated.) + return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; +#elif defined(__APPLE__) + // since macos(10.6), ios(3.2) + return pthread_setname_np(name) == 0; +#else +#error "unsupported platform" +#endif + } - ThreadName(const char* name) - { - if ( (good = get(old_name)) ) + ThreadNameImpl(const char* name) { - snprintf(new_name, 127, "%s", name); - new_name[127] = 0; - prctl(PR_SET_NAME, (unsigned long)new_name, 0, 0); + reset = false; + tid = pthread_self(); + + if (!get(old_name)) + return; + + reset = set(name); + if (reset) + return; + + // Try with a shorter name. 15 is the upper limit supported by Linux, + // other platforms should support a larger value. So 15 should works + // on all platforms. + size_t max_len = 15; + if (std::strlen(name) > max_len) + reset = set(std::string(name, max_len).c_str()); } - } - ~ThreadName() - { - if ( good ) - prctl(PR_SET_NAME, (unsigned long)old_name, 0, 0); - } -}; + ~ThreadNameImpl() + { + if (!reset) + return; + + // ensure it's called on the right thread + if (tid == pthread_self()) + set(old_name); + } + + private: + bool reset; + pthread_t tid; + char old_name[BUFSIZE]; + }; #else -#include "sync.h" + class ThreadNameImpl + { + public: + static const bool DUMMY_IMPL = true; + static const size_t BUFSIZE = 64; -// Fallback version, which simply reports the thread name as -// T, and custom names used with `set` are ignored. -// If you know how to implement this for other systems than -// Linux, you can make another conditional. This one is now -// the "ultimate fallback". + static bool get(char* output) + { + // The default implementation will simply try to get the thread ID + std::ostringstream bs; + bs << "T" << srt::sync::this_thread::get_id(); + size_t s = bs.str().copy(output, BUFSIZE - 1); + output[s] = '\0'; + return true; + } + + static bool set(const char*) { return false; } + + ThreadNameImpl(const char*) {} + + ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version + { + } + }; + +#endif // platform dependent impl + + // Why delegate to impl: + // 1. to make sure implementation on different platforms have the same interface. + // 2. it's simple to add some wrappers like get(const std::string &). + ThreadNameImpl impl; -class ThreadName -{ public: - static const size_t BUFSIZE = 128; + static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL; + static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE; - static bool get(char* output) - { - // The default implementation will simply try to get the thread ID - std::ostringstream bs; - bs << "T" << srt::sync::this_thread::get_id(); - size_t s = bs.str().copy(output, BUFSIZE-1); - output[s] = '\0'; - return true; + // len should >= BUFSIZE + static bool get(char* output) { + return ThreadNameImpl::get(output); } - static bool set(const char*) { return false; } - ThreadName(const char*) + static bool get(std::string& name) { + char buf[BUFSIZE]; + bool ret = get(buf); + if (ret) + name = buf; + return ret; } - ~ThreadName() // just to make it "non-trivially-destructible" for compatibility with normal version + // note: set can fail if name is too long. The upper limit is platform + // dependent. strlen(name) <= 15 should work on most of the platform. + static bool set(const char* name) { return ThreadNameImpl::set(name); } + + static bool set(const std::string& name) { return set(name.c_str()); } + + ThreadName(const char* name) + : impl(name) { } + ThreadName(const std::string& name) + : impl(name.c_str()) + { + } }; - - -#endif #endif diff --git a/test/filelist.maf b/test/filelist.maf index 0cb25cee8..f39dfe2e6 100644 --- a/test/filelist.maf +++ b/test/filelist.maf @@ -19,6 +19,7 @@ test_muxer.cpp test_seqno.cpp test_socket_options.cpp test_sync.cpp +test_threadname.cpp test_timer.cpp test_unitqueue.cpp test_utilities.cpp diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp new file mode 100644 index 000000000..f9b1d1d8c --- /dev/null +++ b/test/test_threadname.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include "gtest/gtest.h" +#include "threadname.h" + +TEST(ThreadName, GetSet) +{ + std::string name("getset"); + char buf[ThreadName::BUFSIZE * 2]; + + memset(buf, 'a', sizeof(buf)); + ASSERT_EQ(ThreadName::get(buf), true); + // ensure doesn't write out-of-range + size_t max = ThreadName::BUFSIZE - 1; + ASSERT_LE(strlen(buf), max); + + if (ThreadName::DUMMY_IMPL) + return; + + ASSERT_EQ(ThreadName::set(name), true); + memset(buf, 'a', sizeof(buf)); + ASSERT_EQ(ThreadName::get(buf), true); + ASSERT_EQ(buf, name); +} + +TEST(ThreadName, AutoReset) +{ + std::string old_name("old"); + std::string new_name("new-name"); + if (ThreadName::DUMMY_IMPL) + { + // just make sure the API is correct + ThreadName t("test"); + return; + } + + ASSERT_EQ(ThreadName::set(old_name), true); + std::string name; + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); + + { + ThreadName threadName(new_name); + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, new_name); + } + + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); + + { + new_name.resize(std::max(512, ThreadName::BUFSIZE * 2), 'z'); + ThreadName threadName(new_name); + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(new_name.compare(0, name.size(), name), 0); + } + + ASSERT_EQ(ThreadName::get(name), true); + ASSERT_EQ(name, old_name); +} From d6f93a16f4adbbf5b85de23e72e2d84cebef053b Mon Sep 17 00:00:00 2001 From: quink-black Date: Wed, 4 Aug 2021 22:37:02 +0800 Subject: [PATCH 119/683] [core] Update srtcore/threadname.h Co-authored-by: Maxim Sharabayko --- srtcore/threadname.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 6fd901aff..2b3964276 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -144,7 +144,10 @@ class ThreadName static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL; static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE; - // len should >= BUFSIZE + /// @brief Print thread ID to the provided buffer. + /// The size of the destination buffer is assumed to be at least ThreadName::BUFSIZE. + /// @param [out] output destination buffer to get thread name + /// @return true on success, false on failure static bool get(char* output) { return ThreadNameImpl::get(output); } From adba7afcea44db5a87debeb150760021108d9db1 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Wed, 4 Aug 2021 23:04:36 +0800 Subject: [PATCH 120/683] [core] Use ThreadName::BUFSIZE in CreateLogLinePrefix() --- srtcore/common.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 389933b26..0e19dc611 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -595,16 +595,20 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) { using namespace std; - char tmp_buf[512]; + SRT_STATIC_ASSERT(ThreadName::BUFSIZE >= sizeof("hh:mm:ss.") * 2, // multiply 2 for some margin + "ThreadName::BUFSIZE is too small to be used for strftime"); + char tmp_buf[ThreadName::BUFSIZE]; if ( !isset(SRT_LOGF_DISABLE_TIME) ) { // Not necessary if sending through the queue. timeval tv; - gettimeofday(&tv, 0); + gettimeofday(&tv, NULL); struct tm tm = SysLocalTime((time_t) tv.tv_sec); - strftime(tmp_buf, 512, "%X.", &tm); - serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) + { + serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + } } string out_prefix; From 6dcbaf0eb302f7eec51a4f2dcb93d3d5855276ab Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Thu, 5 Aug 2021 18:07:49 +1000 Subject: [PATCH 121/683] [build] Cross-compile android from macOS host (#2071) --- configure-data.tcl | 15 +++++++++++-- scripts/build-android/build-android | 34 ++++++++++++++++++++++++----- scripts/build-android/mkssl | 9 ++++---- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/configure-data.tcl b/configure-data.tcl index 66902324a..5c0ee2ee7 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -343,8 +343,19 @@ proc postprocess {} { # Otherwise don't set PKG_CONFIG_PATH and we'll see. } - if { $::HAVE_DARWIN && !$toolchain_changed} { - + set use_brew 0 + if { $::HAVE_DARWIN && !$toolchain_changed } { + set use_brew 1 + } + if { $use_brew } { + foreach item $::cmakeopt { + if { [string first "Android" $item] != -1 } { + set use_brew 0 + break + } + } + } + if { $use_brew } { if { $have_gnutls } { # Use gnutls explicitly, as found in brew set er [catch {exec brew info gnutls} res] diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 3d69dac0c..0d56a8f01 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -52,11 +52,35 @@ else fi fi -# Determine the path of the executing script -BASE_DIR=$(readlink -f $0 | xargs dirname) +SCRIPT_DIR=$(pwd) +HOST_TAG='unknown' +unamestr=$(uname -s) +if [ "$unamestr" = 'Linux' ]; then + SCRIPT_DIR=$(readlink -f $0 | xargs dirname) + HOST_TAG='linux-x86_64' +elif [ "$unamestr" = 'Darwin' ]; then + SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + if [ $(uname -p) = 'arm' ]; then + echo "NDK does not currently support ARM64" + exit 128 + else + HOST_TAG='darwin-x86_64' + fi +fi + +# Write files relative to current location +BASE_DIR=$(pwd) +case "${BASE_DIR}" in + *\ * ) + echo "Your path contains whitespaces, which is not supported by 'make install'." + exit 128 + ;; +esac +cd "${BASE_DIR}" if [ $ENC_LIB = 'openssl' ]; then - $BASE_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION + echo "Building OpenSSL $OPENSSL_VERSION" + $SCRIPT_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION -d $BASE_DIR -h $HOST_TAG elif [ $ENC_LIB = 'mbedtls' ]; then if [ ! -d $BASE_DIR/mbedtls ]; then git clone https://github.com/ARMmbed/mbedtls mbedtls @@ -83,13 +107,13 @@ for build_target in $BUILD_TARGETS; do mkdir -p $JNI_DIR if [ $ENC_LIB = 'mbedtls' ]; then - $BASE_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target + $SCRIPT_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so fi git -C $BASE_DIR/srt clean -fd - $BASE_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index d0305fab7..4e9d0df28 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -1,6 +1,6 @@ #!/bin/sh -while getopts n:o:a:t: option +while getopts n:o:a:t:d:h: option do case "${option}" in @@ -8,13 +8,14 @@ do o) OPENSSL_VERSION=${OPTARG};; a) API_LEVEL=${OPTARG};; t) BUILD_TARGETS=${OPTARG};; + d) OUT_DIR=${OPTARG};; + h) HOST_TAG=${OPTARG};; *) twentytwo=${OPTARG};; esac done BUILD_DIR=/tmp/openssl_android_build -OUT_DIR=$(pwd) if [ ! -d openssl-${OPENSSL_VERSION} ] then @@ -29,7 +30,7 @@ cd openssl-${OPENSSL_VERSION} || exit 128 ##### Prepare Files ##### -sed -i 's/.*-mandroid.*//' Configurations/15-android.conf +sed -i.bak 's/.*-mandroid.*//' Configurations/15-android.conf patch -p1 -N < Date: Thu, 5 Aug 2021 12:49:20 +0200 Subject: [PATCH 122/683] [core] Fixed uninitialized DST socket ID in SHUTDOWN ctrl message --- srtcore/api.h | 11 +++++++++-- srtcore/core.cpp | 9 ++++++--- srtcore/core.h | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index 5750c9db1..e1ba244f3 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -325,14 +325,21 @@ friend class CRendezvousQueue; CEPoll& epoll_ref() { return m_EPoll; } private: -// void init(); - /// Generates a new socket ID. This function starts from a randomly /// generated value (at initialization time) and goes backward with /// with next calls. The possible values come from the range without /// the SRTGROUP_MASK bit, and the group bit is set when the ID is /// generated for groups. It is also internally checked if the /// newly generated ID isn't already used by an existing socket or group. + /// + /// Socket ID value range. + /// - [0]: reserved for handshake procedure. If the destination Socket ID is 0 + /// (destination Socket ID unknown) the packet will be sent to the listening socket + /// or to a socket that is in the rendezvous connection phase. + /// - [1; 2 ^ 30): single socket ID range. + /// - (2 ^ 30; 2 ^ 31): group socket ID range. Effectively any positive number + /// from [1; 2 ^ 30) with bit 30 set to 1. Bit 31 is zero. + /// The most significant bit 31 (sign bit) is left unused so that checking for a value <= 0 identifies an invalid socket ID. /// /// @param group The socket id should be for socket group. /// @return The new socket ID. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cf01d9e35..a96ca4c47 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -243,6 +243,7 @@ void srt::CUDT::construct() // Will be reset to 0 for HSv5, this value is important for HSv4. m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; + m_PeerID = 0; m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -3784,9 +3785,9 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // m_RejectReason already set at worker_ProcessAddressedPacket. LOGC(cnlog.Warn, - log << "processAsyncConnectRequest: REJECT reported from HS processing:" + log << "processAsyncConnectRequest: REJECT reported from HS processing: " << srt_rejectreason_str(m_RejectReason) - << "- not processing further"); //; REQ-TIME LOW"); XXX ? + << " - not processing further"); // m_tsLastReqTime = steady_clock::time_point(); XXX ? return false; } @@ -5957,7 +5958,7 @@ bool srt::CUDT::closeInternal() { if (!m_bShutdown) { - HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer"); + HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer @" << m_PeerID); sendCtrl(UMSG_SHUTDOWN); } @@ -7615,6 +7616,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp break; case UMSG_SHUTDOWN: // 101 - Shutdown + if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. + break; ctrlpkt.pack(pkttype); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); diff --git a/srtcore/core.h b/srtcore/core.h index 9a1ad01f0..6b8472c64 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1029,7 +1029,7 @@ class CUDT private: // Trace struct CoreStats { - time_point tsStartTime; // timestamp when the UDT entity is started + time_point tsStartTime; // timestamp when the UDT entity is started int64_t sentTotal; // total number of sent data packets, including retransmissions int64_t sentUniqTotal; // total number of sent data packets, excluding rexmit and filter control int64_t recvTotal; // total number of received packets From 0fca8741ce319066c1abe62757e1fa7edeeb9d58 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 5 Aug 2021 12:50:13 +0200 Subject: [PATCH 123/683] [tests] Fixed build warning: unused parameter --- test/test_socket_options.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index bf37513ce..eb0b05912 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -349,7 +349,7 @@ bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char { try { const ValueType val = linb::any_cast(inval); - CheckSetSockOpt(entry, sock, val, SRT_ERROR, "[Caller, invalid val]"); + CheckSetSockOpt(entry, sock, val, SRT_ERROR, sock_name); } catch (const linb::bad_any_cast&) { @@ -454,17 +454,18 @@ TEST_F(TestSocketOptions, InvalidVals) // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation for (const auto& entry : g_test_matrix_options) { + const char* desc = "[Caller, invalid val]"; if (entry.dflt_val.type() == typeid(bool)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else if (entry.dflt_val.type() == typeid(int)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else if (entry.dflt_val.type() == typeid(int64_t)) { - EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, "[Caller, invalid val]")); + EXPECT_TRUE(CheckInvalidValues(entry, m_caller_sock, desc)); } else { From 747f288e77e88a9e0f7db70f2fa8477868fa5928 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 6 Aug 2021 13:44:36 +0200 Subject: [PATCH 124/683] [core] Check if CryptoControl exists in createSrtHandshake(..). If it does not, it will lead to a crash, reported in #1979. --- srtcore/core.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a96ca4c47..63e4fadd9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1725,6 +1725,16 @@ bool srt::CUDT::createSrtHandshake( HLOGC(cnlog.Debug, log << "createSrtHandshake: " << (m_config.CryptoSecret.len > 0 ? "Agent uses ENCRYPTION" : "Peer requires ENCRYPTION")); + + if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP)) + { + m_RejectReason = SRT_REJ_IPE; + LOGC(cnlog.Error, log << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." + << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting + << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); + return false; + } + if (srtkm_cmd == SRT_CMD_KMREQ) { bool have_any_keys = false; @@ -1753,7 +1763,6 @@ bool srt::CUDT::createSrtHandshake( { offset += ra_size + 1; ra_size = fillHsExtKMRSP(p + offset - 1, kmdata, kmdata_wordsize); - } else { From f0aa009f0d591c1f74e6d01f84055b69b56a7d04 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 9 Aug 2021 10:47:38 +0200 Subject: [PATCH 125/683] [core] Renamed congestion control event handling functions --- srtcore/congctl.cpp | 52 +++++++++++++++++++++++++++------------------ srtcore/core.cpp | 38 ++++++++++++++++----------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 4612e6852..3b0f923a0 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -81,7 +81,7 @@ class LiveCC: public SrtCongestionControlBase m_zSndAvgPayloadSize = m_zMaxPayloadSize; m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) - m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator + m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize); @@ -92,11 +92,11 @@ class LiveCC: public SrtCongestionControlBase // from receiving thread. parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize)); - /* - * Readjust the max SndPeriod onACK (and onTimeout) - */ - parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer)); - parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck)); + // + // Adjust the max SndPeriod onACK and onTimeout. + // + parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO)); + parent->ConnectSignal(TEV_ACK, SSLOT(onAck)); } bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE @@ -153,17 +153,22 @@ class LiveCC: public SrtCongestionControlBase HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize); } - void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var) + /// @brief On RTO event update an inter-packet send interval. + /// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO. + void onRTO(ETransmissionEvent , EventVariant var) { - if ( var.get() != TEV_CHT_INIT ) + if (var.get() != TEV_CHT_INIT ) updatePktSndPeriod(); } - void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant ) + /// @brief Handle an incoming ACK event. + /// Mainly updates a send interval between packets relying on the maximum BW limit. + void onAck(ETransmissionEvent, EventVariant ) { updatePktSndPeriod(); } + /// @brief Updates a send interval between packets relying on the maximum BW limit. void updatePktSndPeriod() { // packet = payload + header @@ -289,9 +294,9 @@ class FileCC : public SrtCongestionControlBase m_dCWndSize = 16; m_dPktSndPeriod = 1; - parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod)); - parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod)); - parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize)); + parent->ConnectSignal(TEV_ACK, SSLOT(onACK)); + parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(onLossReport)); + parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO)); HLOGC(cclog.Debug, log << "Creating FileCC"); } @@ -305,10 +310,11 @@ class FileCC : public SrtCongestionControlBase return true; } + /// Tells if an early ACK is needed (before the next Full ACK happening every 10ms). + /// In FileCC, treat non-full-payload as an end-of-message (stream) + /// and request ACK to be sent immediately. bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE { - // For FileCC, treat non-full-buffer situation as an end-of-message situation; - // request ACK to be sent immediately. if (pkt.getLength() < m_parent->maxPayloadSize()) { // This is not a regular fixed size packet... @@ -329,9 +335,10 @@ class FileCC : public SrtCongestionControlBase } private: - - // SLOTS - void updateSndPeriod(ETransmissionEvent, EventVariant arg) + /// Handle icoming ACK event. + /// In slow start stage increase CWND. Leave slow start once maximum CWND is reached. + /// In congestion avoidance stage adjust inter packet send interval value to achieve maximum rate. + void onACK(ETransmissionEvent, EventVariant arg) { const int ack = arg.get(); @@ -455,9 +462,10 @@ class FileCC : public SrtCongestionControlBase } - // When a lossreport has been received, it might be due to having - // reached the available bandwidth limit. Slowdown to avoid further losses. - void slowdownSndPeriod(ETransmissionEvent, EventVariant arg) + /// When a lossreport has been received, it might be due to having + /// reached the available bandwidth limit. Slowdown to avoid further losses. + /// Leave the slow start stage if it was active. + void onLossReport(ETransmissionEvent, EventVariant arg) { const int32_t* losslist = arg.get_ptr(); size_t losslist_size = arg.get_len(); @@ -559,7 +567,9 @@ class FileCC : public SrtCongestionControlBase } } - void speedupToWindowSize(ETransmissionEvent, EventVariant arg) + /// @brief On retransmission timeout leave slow start stage if it was active. + /// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO. + void onRTO(ETransmissionEvent, EventVariant arg) { ECheckTimerStage stg = arg.get(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 63e4fadd9..c11e66064 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10899,21 +10899,21 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) { - /* There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. - * - * LATEREXMIT is only used with FileCC. - * The mode is triggered when some time has passed since the last ACK from - * the receiver, while there is still some unacknowledged data in the sender's buffer, - * and the loss list is empty. - * - * FASTREXMIT is only used with LiveCC. - * The mode is triggered if the receiver does not send periodic NAK reports, - * when some time has passed since the last ACK from the receiver, - * while there is still some unacknowledged data in the sender's buffer. - * - * In case the above conditions are met, the unacknowledged packets - * in the sender's buffer will be added to loss list and retransmitted. - */ + // There are two algorithms of blind packet retransmission: LATEREXMIT and FASTREXMIT. + // + // LATEREXMIT is only used with FileCC. + // The RTO is triggered when some time has passed since the last ACK from + // the receiver, while there is still some unacknowledged data in the sender's buffer, + // and the loss list is empty at the moment of RTO (nothing to retransmit yet). + // + // FASTREXMIT is only used with LiveCC. + // The RTO is triggered if the receiver is not configured to send periodic NAK reports, + // when some time has passed since the last ACK from the receiver, + // while there is still some unacknowledged data in the sender's buffer. + // + // In case the above conditions are met, the unacknowledged packets + // in the sender's buffer will be added to the SND loss list and retransmitted. + // const uint64_t rtt_syn = (m_iSRTT + 4 * m_iRTTVar + 2 * COMM_SYN_INTERVAL_US); const uint64_t exp_int_us = (m_iReXmitCount * rtt_syn + COMM_SYN_INTERVAL_US); @@ -10926,19 +10926,19 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) if (m_pSndBuffer->getCurrBufSize() <= 0) return; - const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; - const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; + const bool is_laterexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_LATEREXMIT; // FileCC + const bool is_fastrexmit = m_CongCtl->rexmitMethod() == SrtCongestion::SRM_FASTREXMIT; // LiveCC // If the receiver will send periodic NAK reports, then FASTREXMIT (live) is inactive. // TODO: Probably some method of "blind rexmit" MUST BE DONE, when TLPKTDROP is off. if (is_fastrexmit && m_bPeerNakReport) return; - // Schedule for retransmission IF: + // Schedule a retransmission IF: // - there are packets in flight (getFlightSpan() > 0); // - in case of LATEREXMIT (File Mode): the sender loss list is empty // (the receiver didn't send any LOSSREPORT, or LOSSREPORT was lost on track). - // - in case of FASTREXMIT (Live Mode): there is the latency constraint, therefore + // - in case of FASTREXMIT (Live Mode): the RTO (rtt_syn) was triggered, therefore // schedule unacknowledged packets for retransmission regardless of the loss list emptiness. if (getFlightSpan() > 0 && (!is_laterexmit || m_pSndLossList->getLossLength() == 0)) { From 252337c129c011cada22dcd1772beae40579989d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 9 Aug 2021 10:54:58 +0200 Subject: [PATCH 126/683] [core] Moved congestion control into the srt namespace --- srtcore/congctl.cpp | 9 ++++++--- srtcore/congctl.h | 11 ++++------- srtcore/socketconfig.cpp | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 3b0f923a0..2301dfc68 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -34,10 +34,11 @@ #include "logging.h" using namespace std; -using namespace srt; using namespace srt::sync; using namespace srt_logging; +namespace srt { + SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent) { m_parent = parent; @@ -399,7 +400,7 @@ class FileCC : public SrtCongestionControlBase else { double inc = 0; - const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point + const int loss_bw = static_cast(2 * (1000000 / m_dLastDecPeriod)); // 2 times last loss point const int bw_pktps = min(loss_bw, m_parent->bandwidth()); int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod); @@ -500,7 +501,7 @@ class FileCC : public SrtCongestionControlBase m_bLoss = true; // TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo()); - const int pktsInFlight = m_parent->SRTT() / m_dPktSndPeriod; + const int pktsInFlight = static_cast(m_parent->SRTT() / m_dPktSndPeriod); const int numPktsLost = m_parent->sndLossLength(); const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0; @@ -656,3 +657,5 @@ SrtCongestion::~SrtCongestion() { dispose(); } + +} // namespace srt diff --git a/srtcore/congctl.h b/srtcore/congctl.h index 0ad835783..b957dbdd0 100644 --- a/srtcore/congctl.h +++ b/srtcore/congctl.h @@ -17,10 +17,9 @@ #include namespace srt { - class CUDT; -} -class SrtCongestionControlBase; +class CUDT; +class SrtCongestionControlBase; typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent); class SrtCongestion @@ -131,9 +130,7 @@ class SrtCongestion }; }; -namespace srt { - class CPacket; -} +class CPacket; class SrtCongestionControlBase { @@ -224,6 +221,6 @@ class SrtCongestionControlBase }; - +} // namespace srt #endif diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index a62881cd7..820a4334e 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -570,7 +570,7 @@ struct CSrtConfigSetter if (val == "vod") val = "file"; - bool res = SrtCongestion::exists(val); + bool res = srt::SrtCongestion::exists(val); if (!res) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); From d7dcf0cab7e7a44d9c2daf387f809585fffc715f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 11 Aug 2021 16:41:34 +0200 Subject: [PATCH 127/683] [core] Check if socket is still open before replying to HS --- srtcore/core.cpp | 2 ++ srtcore/queue.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c11e66064..7c0f3acf5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3769,6 +3769,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, bool status = true; ScopedLock cg(m_ConnectionLock); + if (!m_bOpened) // Check the socket has not been closed before already. + return false; if (cst == CONN_RENDEZVOUS) { diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 2b0fd2c74..03ba97e47 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -989,11 +989,12 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst for (vector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) { HLOGC(cnlog.Debug, log << "updateConnStatus: COMPLETING dep objects update on failed @" << i->id); - /* - * Setting m_bConnecting to false but keeping socket in rendezvous queue is not a good idea. - * Next CUDT::close will not remove it from rendezvous queue (because !m_bConnecting) - * and may crash on next pass. - */ + // + // Setting m_bConnecting to false, and need to remove the socket from the rendezvous queue + // because the next CUDT::close will not remove it from the queue when m_bConnecting = false, + // and may crash on next pass. + // + // TODO: maybe lock i->u->m_ConnectionLock? i->u->m_bConnecting = false; remove(i->u->m_SocketID); From 7728b7000fb720d1d974f1d24db07b4ce82adf95 Mon Sep 17 00:00:00 2001 From: Laurent Bigonville Date: Thu, 12 Aug 2021 15:10:13 +0200 Subject: [PATCH 128/683] [core] Prefer the endian.h header file from glibc if available On the Debian kfreebsd port, both endian.h and sys/endian.h are available. After discussing with the kfreebsd port maintainer and since this is userspace related, we should prefer the one from the glibc See: #2066 --- srtcore/utilities.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index b1f21fbb2..60a286ec0 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -57,7 +57,7 @@ written by #endif -#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) +#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) # include @@ -115,7 +115,7 @@ written by # include -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) # include From f1c96d9868c4b9daa05d07bc8dca1b681cad314b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 12 Aug 2021 11:58:56 +0200 Subject: [PATCH 129/683] [core] Check Win QPC frequency is non-zero --- srtcore/sync_posix.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 960a1dfad..c44fe86c2 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -86,6 +86,12 @@ static int64_t get_cpu_frequency() if (QueryPerformanceFrequency(&ccf)) { frequency = ccf.QuadPart / 1000000; // counts per microsecond + if (frequency == 0) + { + LOGC(inlog.Warn, log << "Win QPC frequency of " << ccf.QuadPart + << " counts/s is below the required 1 us accuracy. Please consider using C++11 timing (-DENABLE_STDCXX_SYNC=ON) instead."); + frequency = 1; // set back to 1 to avoid division by zero. + } } else { From 6ca1e0daa59ec0d7983ab8da002a5f7281bd2a2b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 12:16:00 +0200 Subject: [PATCH 130/683] [tests] Fixed possible TestIPv6 hangups if connection fails --- test/test_ipv6.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 820288fa8..69a51bfbb 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -48,11 +48,14 @@ class TestIPv6 { sockaddr_any sa (family); sa.hport(m_listen_port); - ASSERT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); + EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; - ASSERT_NE(srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa), SRT_ERROR); + const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + if (connect_res == SRT_ERROR) + srt_close(m_listener_sock); PrintAddresses(m_caller_sock, "CALLER"); } @@ -61,7 +64,7 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { - ASSERT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; + EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; } @@ -70,16 +73,23 @@ class TestIPv6 sockaddr_any sc1; SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); - EXPECT_NE(accepted_sock, SRT_INVALID_SOCK); + EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); + if (accepted_sock == SRT_INVALID_SOCK) { + return sockaddr_any(); + } PrintAddresses(accepted_sock, "ACCEPTED"); sockaddr_any sn; EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); - - int32_t ipv6_zero [] = {0, 0, 0, 0}; - EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0) - << "EMPTY address in srt_getsockname"; + EXPECT_NE(sn.get_addr(), nullptr); + + if (sn.get_addr() != nullptr) + { + const int32_t ipv6_zero[] = { 0, 0, 0, 0 }; + EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0) + << "EMPTY address in srt_getsockname"; + } srt_close(accepted_sock); return sn; From b2141220f5e441a9cee99f8d80b2a32bb38f7577 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 11:48:32 +0200 Subject: [PATCH 131/683] [core] Fixed int64_t to long type cast --- srtcore/api.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d38442590..264192155 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2171,8 +2171,8 @@ int srt::CUDTUnited::select( { const steady_clock::time_point entertime = steady_clock::now(); - const long timeo_us = timeout - ? timeout->tv_sec * 1000000 + timeout->tv_usec + const int64_t timeo_us = timeout + ? static_cast(timeout->tv_sec) * 1000000 + timeout->tv_usec : -1; const steady_clock::duration timeo(microseconds_from(timeo_us)); @@ -2285,7 +2285,7 @@ int srt::CUDTUnited::selectEx( { const steady_clock::time_point entertime = steady_clock::now(); - const long timeo_us = msTimeOut >= 0 + const int64_t timeo_us = msTimeOut >= 0 ? msTimeOut * 1000 : -1; const steady_clock::duration timeo(microseconds_from(timeo_us)); From bc5a6429473648a70396a9acc229e04fd5e4d0a9 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 14:37:20 +0200 Subject: [PATCH 132/683] [tests] Fixed range loop construct warning --- test/test_socket_options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index eb0b05912..d010de510 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -345,7 +345,7 @@ bool CheckMaxValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* des template bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char* sock_name) { - for (const auto inval : entry.invalid_vals) + for (const auto& inval : entry.invalid_vals) { try { const ValueType val = linb::any_cast(inval); From ae11c18453fd46f15606e601b7232f48e3d4a464 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 13 Aug 2021 11:45:35 +0200 Subject: [PATCH 133/683] [core] CUDTSocket now owns a member CUDT class (composition). --- srtcore/api.cpp | 244 ++++++++++++++++++++++------------------------ srtcore/api.h | 31 +++++- srtcore/core.cpp | 20 ++-- srtcore/group.cpp | 18 ++-- 4 files changed, 164 insertions(+), 149 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 264192155..7f6d7ecaa 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -96,10 +96,6 @@ void srt::CUDTSocket::construct() srt::CUDTSocket::~CUDTSocket() { - - delete m_pUDT; - m_pUDT = NULL; - releaseMutex(m_AcceptLock); releaseCond(m_AcceptCond); releaseMutex(m_ControlLock); @@ -113,11 +109,11 @@ SRT_SOCKSTATUS srt::CUDTSocket::getStatus() // In this case m_bConnected is also false. Both checks are required to avoid hitting // a regular state transition from CONNECTING to CONNECTED. - if (m_pUDT->m_bBroken) + if (m_UDT.m_bBroken) return SRTS_BROKEN; // Connecting timed out - if ((m_Status == SRTS_CONNECTING) && !m_pUDT->m_bConnecting && !m_pUDT->m_bConnected) + if ((m_Status == SRTS_CONNECTING) && !m_UDT.m_bConnecting && !m_UDT.m_bConnected) return SRTS_BROKEN; return m_Status; @@ -128,10 +124,10 @@ void srt::CUDTSocket::breakSocket_LOCKED() { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. - m_pUDT->m_bBroken = true; - m_pUDT->m_iBrokenCounter = 0; + m_UDT.m_bBroken = true; + m_UDT.m_iBrokenCounter = 0; HLOGC(smlog.Debug, log << "@" << m_SocketID << " CLOSING AS SOCKET"); - m_pUDT->closeInternal(); + m_UDT.closeInternal(); setClosed(); } @@ -148,16 +144,16 @@ void srt::CUDTSocket::setClosed() void srt::CUDTSocket::setBrokenClosed() { - m_pUDT->m_iBrokenCounter = 60; - m_pUDT->m_bBroken = true; + m_UDT.m_iBrokenCounter = 60; + m_UDT.m_bBroken = true; setClosed(); } bool srt::CUDTSocket::readReady() { - if (m_pUDT->m_bConnected && m_pUDT->m_pRcvBuffer->isRcvDataReady()) + if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) return true; - if (m_pUDT->m_bListening) + if (m_UDT.m_bListening) { return m_QueuedSockets.size() > 0; } @@ -167,14 +163,14 @@ bool srt::CUDTSocket::readReady() bool srt::CUDTSocket::writeReady() const { - return (m_pUDT->m_bConnected - && (m_pUDT->m_pSndBuffer->getCurrBufSize() < m_pUDT->m_config.iSndBufSize)) + return (m_UDT.m_bConnected + && (m_UDT.m_pSndBuffer->getCurrBufSize() < m_UDT.m_config.iSndBufSize)) || broken(); } bool srt::CUDTSocket::broken() const { - return m_pUDT->m_bBroken || !m_pUDT->m_bConnected; + return m_UDT.m_bBroken || !m_UDT.m_bConnected; } //////////////////////////////////////////////////////////////////////////////// @@ -239,7 +235,7 @@ int srt::CUDTUnited::startup() ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return 1; + return 1; // Global initialization code #ifdef _WIN32 @@ -290,10 +286,10 @@ int srt::CUDTUnited::cleanup() ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) - return 0; + return 0; if (!m_bGCStatus) - return 0; + return 0; m_bClosing = true; // NOTE: we can do relaxed signaling here because @@ -436,7 +432,6 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) try { ns = new CUDTSocket; - ns->m_pUDT = new CUDT(ns); } catch (...) { @@ -455,8 +450,8 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) } ns->m_Status = SRTS_INIT; ns->m_ListenSocket = 0; - ns->m_pUDT->m_SocketID = ns->m_SocketID; - ns->m_pUDT->m_pCache = m_pCache; + ns->core().m_SocketID = ns->m_SocketID; + ns->core().m_pCache = m_pCache; try { @@ -507,7 +502,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // if this connection has already been processed if ((ns = locatePeer(peer, w_hs.m_iID, w_hs.m_iISN)) != NULL) { - if (ns->m_pUDT->m_bBroken) + if (ns->core().m_bBroken) { // last connection from the "peer" address has been broken ns->setClosed(); @@ -523,15 +518,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p << "newConnection: located a WORKING peer @" << w_hs.m_iID << " - ADAPTING."); - w_hs.m_iISN = ns->m_pUDT->m_iISN; - w_hs.m_iMSS = ns->m_pUDT->MSS(); - w_hs.m_iFlightFlagSize = ns->m_pUDT->m_config.iFlightFlagSize; + w_hs.m_iISN = ns->core().m_iISN; + w_hs.m_iMSS = ns->core().MSS(); + w_hs.m_iFlightFlagSize = ns->core().m_config.iFlightFlagSize; w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; // Report the original UDT because it will be // required to complete the HS data for conclusion response. - w_acpu = ns->m_pUDT; + w_acpu = &ns->core(); return 0; @@ -554,8 +549,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p try { - ns = new CUDTSocket; - ns->m_pUDT = new CUDT(ns, *(ls->m_pUDT)); + ns = new CUDTSocket(*ls); // No need to check the peer, this is the address from which the request has come. ns->m_PeerAddr = peer; } @@ -567,7 +561,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p return -1; } - ns->m_pUDT->m_RejectReason = SRT_REJ_UNKNOWN; // pre-set a universal value + ns->core().m_RejectReason = SRT_REJ_UNKNOWN; // pre-set a universal value try { @@ -585,13 +579,13 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p } ns->m_ListenSocket = listen; - ns->m_pUDT->m_SocketID = ns->m_SocketID; + ns->core().m_SocketID = ns->m_SocketID; ns->m_PeerID = w_hs.m_iID; ns->m_iISN = w_hs.m_iISN; HLOGC(cnlog.Debug, log << "newConnection: DATA: lsnid=" << listen - << " id=" << ns->m_pUDT->m_SocketID - << " peerid=" << ns->m_pUDT->m_PeerID + << " id=" << ns->core().m_SocketID + << " peerid=" << ns->core().m_PeerID << " ISN=" << ns->m_iISN); int error = 0; @@ -621,11 +615,11 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p m_Sockets[ns->m_SocketID] = ns; } - if (ls->m_pUDT->m_cbAcceptHook) + if (ls->core().m_cbAcceptHook) { - if (!ls->m_pUDT->runAcceptHook(ns->m_pUDT, peer.get(), w_hs, hspkt)) + if (!ls->core().runAcceptHook(&ns->core(), peer.get(), w_hs, hspkt)) { - w_error = ns->m_pUDT->m_RejectReason; + w_error = ns->core().m_RejectReason; error = 1; goto ERR_ROLLBACK; @@ -633,15 +627,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p } // bind to the same addr of listening socket - ns->m_pUDT->open(); + ns->core().open(); updateListenerMux(ns, ls); - ns->m_pUDT->acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); + ns->core().acceptAndRespond(ls->m_SelfAddr, peer, hspkt, (w_hs)); } catch (...) { // Extract the error that was set in this new failed entity. - w_error = ns->m_pUDT->m_RejectReason; + w_error = ns->core().m_RejectReason; error = 1; goto ERR_ROLLBACK; } @@ -651,11 +645,11 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // copy address information of local node // Precisely, what happens here is: // - Get the IP address and port from the system database - ns->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((ns->m_SelfAddr)); + ns->core().m_pSndQueue->m_pChannel->getSockAddr((ns->m_SelfAddr)); // - OVERWRITE just the IP address itself by a value taken from piSelfIP // (the family is used exactly as the one taken from what has been returned // by getsockaddr) - CIPAddress::pton((ns->m_SelfAddr), ns->m_pUDT->m_piSelfIP, peer); + CIPAddress::pton((ns->m_SelfAddr), ns->core().m_piSelfIP, peer); { // protect the m_PeerRec structure (and group existence) @@ -757,7 +751,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p /* SETUP HERE IF NEEDED - ns->m_pUDT->m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); + ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); */ } else @@ -783,7 +777,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p HLOGC(cnlog.Debug, log << "ACCEPT: new socket @" << ns->m_SocketID << " submitted for acceptance"); // acknowledge users waiting for new connections on the listening socket - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_ACCEPT, true); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); CGlobEvent::triggerEvent(); @@ -803,7 +797,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // acknowledge INTERNAL users waiting for new connections on the listening socket // that are reported when a new socket is connected within an already connected group. - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_UPDATE, true); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); CGlobEvent::triggerEvent(); } @@ -823,7 +817,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p #endif SRTSOCKET id = ns->m_SocketID; - ns->m_pUDT->closeInternal(); + ns->core().closeInternal(); ns->setClosed(); // The mapped socket should be now unmapped to preserve the situation that @@ -862,7 +856,7 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ try { CUDTSocket* s = locateSocket(lsn, ERH_THROW); - s->m_pUDT->installAcceptHook(hook, opaq); + s->core().installAcceptHook(hook, opaq); } catch (CUDTException& e) { @@ -891,7 +885,7 @@ int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_ } #endif CUDTSocket* s = locateSocket(u, ERH_THROW); - s->m_pUDT->installConnectHook(hook, opaq); + s->core().installConnectHook(hook, opaq); } catch (CUDTException& e) { @@ -927,12 +921,12 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) if (s->m_Status != SRTS_INIT) throw CUDTException(MJ_NOTSUP, MN_NONE, 0); - s->m_pUDT->open(); + s->core().open(); updateMux(s, name); s->m_Status = SRTS_OPENED; // copy address information of local node - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); return 0; } @@ -956,12 +950,12 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->m_pUDT->open(); + s->core().open(); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; // copy address information of local node - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); return 0; } @@ -998,14 +992,14 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // [[using assert(s->m_Status == OPENED)]]; // listen is not supported in rendezvous connection setup - if (s->m_pUDT->m_config.bRendezvous) + if (s->core().m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDEZVOUS, 0); s->m_uiBackLog = backlog; // [[using assert(s->m_Status == OPENED)]]; // (still, unchanged) - s->m_pUDT->setListenState(); // propagates CUDTException, + s->core().setListenState(); // propagates CUDTException, // if thrown, remains in OPENED state if so. s->m_Status = SRTS_LISTENING; @@ -1080,7 +1074,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int } // no "accept" in rendezvous connection setup - if (ls->m_pUDT->m_config.bRendezvous) + if (ls->core().m_config.bRendezvous) { LOGC(cnlog.Fatal, log << "CUDTUnited::accept: RENDEZVOUS flag passed through check in srt_listen when it set listen state"); // This problem should never happen because `srt_listen` function should have @@ -1098,7 +1092,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int UniqueLock accept_lock(ls->m_AcceptLock); CSync accept_sync(ls->m_AcceptCond, accept_lock); - if ((ls->m_Status != SRTS_LISTENING) || ls->m_pUDT->m_bBroken) + if ((ls->m_Status != SRTS_LISTENING) || ls->core().m_bBroken) { // This socket has been closed. accepted = true; @@ -1110,7 +1104,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int ls->m_QueuedSockets.erase(b); accepted = true; } - else if (!ls->m_pUDT->m_config.bSynRecving) + else if (!ls->core().m_config.bSynRecving) { accepted = true; } @@ -1119,13 +1113,13 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int accept_sync.wait(); if (ls->m_QueuedSockets.empty()) - m_EPoll.update_events(listen, ls->m_pUDT->m_sPollID, SRT_EPOLL_ACCEPT, false); + m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, false); } if (u == CUDT::INVALID_SOCK) { // non-blocking receiving, no connection available - if (!ls->m_pUDT->m_config.bSynRecving) + if (!ls->core().m_config.bSynRecving) { LOGC(cnlog.Error, log << "srt_accept: no pending connection available at the moment"); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); @@ -1150,7 +1144,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int // and the already accepted socket has successfully joined // the mirror group. If so, RETURN THE GROUP ID, not the socket ID. #if ENABLE_EXPERIMENTAL_BONDING - if (ls->m_pUDT->m_config.iGroupConnect == 1 && s->m_GroupOf) + if (ls->core().m_config.iGroupConnect == 1 && s->m_GroupOf) { // Put a lock to protect the group against accidental deletion // in the meantime. @@ -1370,7 +1364,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i if (pg->m_cbConnectHook) { // Derive the connect hook by the socket, if set on the group - ns->m_pUDT->m_cbConnectHook = pg->m_cbConnectHook; + ns->core().m_cbConnectHook = pg->m_cbConnectHook; } SRT_SocketOptionObject* config = targets[tii].config; @@ -1494,7 +1488,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // be probably still in use to exchange information about // packets assymetrically lost. But for no other purpose. /* - ns->m_pUDT->m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); + ns->core().m_cbPacketArrival.set(ns->m_pUDT, &CUDT::groupPacketArrival); */ int isn = g.currentSchedSequence(); @@ -1508,14 +1502,14 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } // Set it the groupconnect option, as all in-group sockets should have. - ns->m_pUDT->m_config.iGroupConnect = 1; + ns->core().m_config.iGroupConnect = 1; // Every group member will have always nonblocking // (this implies also non-blocking connect/accept). // The group facility functions will block when necessary // using epoll_wait. - ns->m_pUDT->m_config.bSynRecving = false; - ns->m_pUDT->m_config.bSynSending = false; + ns->core().m_config.bSynRecving = false; + ns->core().m_config.bSynSending = false; HLOGC(aclog.Debug, log << "groupConnect: NOTIFIED AS PENDING @" << sid << " both read and write"); // If this socket is not to block the current connect process, @@ -1832,7 +1826,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i if (s->m_Status == SRTS_INIT) { - if (s->m_pUDT->m_config.bRendezvous) + if (s->core().m_config.bRendezvous) throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); // If bind() was done first on this socket, then the @@ -1840,7 +1834,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i // same thing as bind() does, just with empty address so that // the binding parameters are autoselected. - s->m_pUDT->open(); + s->core().open(); sockaddr_any autoselect_sa (target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). @@ -1878,7 +1872,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i { // record peer address s->m_PeerAddr = target_addr; - s->m_pUDT->startConnect(target_addr, forced_isn); + s->core().startConnect(target_addr, forced_isn); } catch (CUDTException& e) // Interceptor, just to change the state. { @@ -1962,23 +1956,21 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) int srt::CUDTUnited::close(CUDTSocket* s) { - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); - + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing from listening, closing CUDT)"); - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); - - const bool synch_close_snd = s->m_pUDT->m_config.bSynSending; + const bool synch_close_snd = s->core().m_config.bSynSending; SRTSOCKET u = s->m_SocketID; if (s->m_Status == SRTS_LISTENING) { - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) return 0; s->m_tsClosureTimeStamp = steady_clock::now(); - s->m_pUDT->m_bBroken = true; + s->core().m_bBroken = true; // Change towards original UDT: // Leave all the closing activities for garbageCollect to happen, @@ -1989,8 +1981,8 @@ int srt::CUDTUnited::close(CUDTSocket* s) // be unable to bind to this port that the about-to-delete listener // is currently occupying (due to blocked slot in the RcvQueue). - HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing listener immediately)"); - s->m_pUDT->notListening(); + HLOGC(smlog.Debug, log << s->core().CONID() << " CLOSING (removing listener immediately)"); + s->core().notListening(); // broadcast all "accept" waiting CSync::lock_broadcast(s->m_AcceptCond, s->m_AcceptLock); @@ -2002,10 +1994,10 @@ int srt::CUDTUnited::close(CUDTSocket* s) // may block INDEFINITELY. As long as it's acceptable to block the // call to srt_close(), and all functions in all threads where this // very socket is used, this shall not block the central database. - s->m_pUDT->closeInternal(); + s->core().closeInternal(); // synchronize with garbage collection. - HLOGC(smlog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->m_pUDT->CONID() << ". Acquiring GLOBAL control lock"); + HLOGC(smlog.Debug, log << "@" << u << "U::close done. GLOBAL CLOSE: " << s->core().CONID() << ". Acquiring GLOBAL control lock"); ScopedLock manager_cg(m_GlobControlLock); // since "s" is located before m_GlobControlLock, locate it again in case // it became invalid @@ -2052,7 +2044,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) HLOGC(smlog.Debug, log << "@" << u << " GLOBAL CLOSING: sync-waiting for releasing sender resources..."); for (;;) { - CSndBuffer* sb = s->m_pUDT->m_pSndBuffer; + CSndBuffer* sb = s->core().m_pSndBuffer; // Disconnected from buffer - nothing more to check. if (!sb) @@ -2080,7 +2072,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } if (!isgone) { - isgone = !s->m_pUDT->m_bOpened; + isgone = !s->core().m_bOpened; } if (isgone) { @@ -2131,7 +2123,7 @@ void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_ if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - if (!s->m_pUDT->m_bConnected || s->m_pUDT->m_bBroken) + if (!s->core().m_bConnected || s->core().m_bBroken) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); const int len = s->m_PeerAddr.size(); @@ -2152,7 +2144,7 @@ void srt::CUDTUnited::getsockname(const SRTSOCKET u, sockaddr* pw_name, int* pw_ if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); if (s->m_Status == SRTS_INIT) @@ -2306,7 +2298,7 @@ int srt::CUDTUnited::selectEx( { CUDTSocket* s = locateSocket(*i); - if ((!s) || s->m_pUDT->m_bBroken || (s->m_Status == SRTS_CLOSED)) + if ((!s) || s->core().m_bBroken || (s->m_Status == SRTS_CLOSED)) { if (exceptfds) { @@ -2318,10 +2310,10 @@ int srt::CUDTUnited::selectEx( if (readfds) { - if ((s->m_pUDT->m_bConnected - && s->m_pUDT->m_pRcvBuffer->isRcvDataReady() + if ((s->core().m_bConnected + && s->core().m_pRcvBuffer->isRcvDataReady() ) - || (s->m_pUDT->m_bListening + || (s->core().m_bListening && (s->m_QueuedSockets.size() > 0))) { readfds->push_back(s->m_SocketID); @@ -2331,9 +2323,9 @@ int srt::CUDTUnited::selectEx( if (writefds) { - if (s->m_pUDT->m_bConnected - && (s->m_pUDT->m_pSndBuffer->getCurrBufSize() - < s->m_pUDT->m_config.iSndBufSize)) + if (s->core().m_bConnected + && (s->core().m_pSndBuffer->getCurrBufSize() + < s->core().m_config.iSndBufSize)) { writefds->push_back(s->m_SocketID); ++ count; @@ -2393,7 +2385,7 @@ int srt::CUDTUnited::epoll_add_usock( int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) { int ret = m_EPoll.update_usock(eid, s->m_SocketID, events); - s->m_pUDT->addEPoll(eid); + s->core().addEPoll(eid); return ret; } @@ -2434,7 +2426,7 @@ int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) // Needed internal access! int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { - return epoll_remove_entity(eid, s->m_pUDT); + return epoll_remove_entity(eid, &s->core()); } #if ENABLE_EXPERIMENTAL_BONDING @@ -2461,7 +2453,7 @@ int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { s = locateSocket(u); if (s) - return epoll_remove_entity(eid, s->m_pUDT); + return epoll_remove_entity(eid, &s->core()); } LOGC(ealog.Error, log << "remove_usock: @" << u @@ -2623,7 +2615,7 @@ void srt::CUDTUnited::checkBrokenSockets() CUDTSocket* s = i->second; // check broken connection - if (s->m_pUDT->m_bBroken) + if (s->core().m_bBroken) { if (s->m_Status == SRTS_LISTENING) { @@ -2635,21 +2627,21 @@ void srt::CUDTUnited::checkBrokenSockets() continue; } } - else if ((s->m_pUDT->m_pRcvBuffer != NULL) + else if ((s->core().m_pRcvBuffer != NULL) // FIXED: calling isRcvDataAvailable() just to get the information // whether there are any data waiting in the buffer, // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when // this function is called (isRcvDataReady also checks if the // available data is "ready to play"). - && s->m_pUDT->m_pRcvBuffer->isRcvDataAvailable()) + && s->core().m_pRcvBuffer->isRcvDataAvailable()) { - const int bc = s->m_pUDT->m_iBrokenCounter.load(); + const int bc = s->core().m_iBrokenCounter.load(); if (bc > 0) { // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): // %d\n", i->first); // if there is still data in the receiver buffer, wait longer - s->m_pUDT->m_iBrokenCounter.store(bc - 1); + s->core().m_iBrokenCounter.store(bc - 1); continue; } } @@ -2688,16 +2680,16 @@ void srt::CUDTUnited::checkBrokenSockets() j != m_ClosedSockets.end(); ++ j) { // HLOGF(smlog.Debug, "checking CLOSED socket: %d\n", j->first); - if (!is_zero(j->second->m_pUDT->m_tsLingerExpiration)) + if (!is_zero(j->second->core().m_tsLingerExpiration)) { // asynchronous close: - if ((!j->second->m_pUDT->m_pSndBuffer) - || (0 == j->second->m_pUDT->m_pSndBuffer->getCurrBufSize()) - || (j->second->m_pUDT->m_tsLingerExpiration <= steady_clock::now())) + if ((!j->second->core().m_pSndBuffer) + || (0 == j->second->core().m_pSndBuffer->getCurrBufSize()) + || (j->second->core().m_tsLingerExpiration <= steady_clock::now())) { HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << j->second->m_SocketID); - j->second->m_pUDT->m_tsLingerExpiration = steady_clock::time_point(); - j->second->m_pUDT->m_bClosing = true; + j->second->core().m_tsLingerExpiration = steady_clock::time_point(); + j->second->core().m_bClosing = true; j->second->m_tsClosureTimeStamp = steady_clock::now(); } } @@ -2708,7 +2700,7 @@ void srt::CUDTUnited::checkBrokenSockets() const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; if (closed_ago > seconds_from(1)) { - CRNode* rnode = j->second->m_pUDT->m_pRNode; + CRNode* rnode = j->second->core().m_pRNode; if (!rnode || !rnode->m_bOnList) { HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " @@ -2744,11 +2736,11 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // still be under processing in the sender/receiver worker // threads. If that's the case, SKIP IT THIS TIME. The // socket will be checked next time the GC rollover starts. - CSNode* sn = s->m_pUDT->m_pSNode; + CSNode* sn = s->core().m_pSNode; if (sn && sn->m_iHeapLoc != -1) return; - CRNode* rn = s->m_pUDT->m_pRNode; + CRNode* rn = s->core().m_pRNode; if (rn && rn->m_bOnList) return; @@ -2804,14 +2796,14 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) * remains forever causing epoll_wait to unblock continuously for inexistent * sockets. Get rid of all events for this socket. */ - m_EPoll.update_events(u, s->m_pUDT->m_sPollID, + m_EPoll.update_events(u, s->core().m_sPollID, SRT_EPOLL_IN|SRT_EPOLL_OUT|SRT_EPOLL_ERR, false); // delete this one m_ClosedSockets.erase(i); HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); - s->m_pUDT->closeInternal(); + s->core().closeInternal(); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; @@ -2850,7 +2842,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { - w_m.m_mcfg = s->m_pUDT->m_config; + w_m.m_mcfg = s->core().m_config; w_m.m_iIPversion = af; w_m.m_iRefCount = 1; w_m.m_iID = s->m_SocketID; @@ -2858,8 +2850,8 @@ void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) { - w_s->m_pUDT->m_pSndQueue = fw_sm.m_pSndQueue; - w_s->m_pUDT->m_pRcvQueue = fw_sm.m_pRcvQueue; + w_s->core().m_pSndQueue = fw_sm.m_pSndQueue; + w_s->core().m_pRcvQueue = fw_sm.m_pRcvQueue; w_s->m_iMuxID = fw_sm.m_iID; sockaddr_any sa; fw_sm.m_pChannel->getSockAddr((sa)); @@ -2869,7 +2861,7 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) { - return m.m_mcfg.bReuseAddr && m.m_mcfg == s->m_pUDT->m_config; + return m.m_mcfg.bReuseAddr && m.m_mcfg == s->core().m_config; } void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) @@ -2922,14 +2914,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // Still, for ANY you need either the same family, or open // for families. - if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->m_pUDT->m_config.iIpV6Only) + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->core().m_config.iIpV6Only) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with existing IPv6 wildcard binding: " << sa.str()); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } - if ((m.m_mcfg.iIpV6Only == 0 || s->m_pUDT->m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + if ((m.m_mcfg.iIpV6Only == 0 || s->core().m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str() @@ -3026,7 +3018,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; m.m_pRcvQueue->init( - 32, s->m_pUDT->maxPayloadSize(), m.m_iIPversion, 1024, + 32, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); // Rewrite the port here, as it might be only known upon return @@ -3127,8 +3119,8 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) { // reuse the existing multiplexer ++ mux->m_iRefCount; - s->m_pUDT->m_pSndQueue = mux->m_pSndQueue; - s->m_pUDT->m_pRcvQueue = mux->m_pRcvQueue; + s->core().m_pSndQueue = mux->m_pSndQueue; + s->core().m_pRcvQueue = mux->m_pRcvQueue; s->m_iMuxID = mux->m_iID; return true; } @@ -3745,8 +3737,8 @@ int srt::CUDT::getsockopt( } #endif - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->getOpt(optname, (pw_optval), (*pw_optlen)); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } catch (const CUDTException& e) @@ -3777,8 +3769,8 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt } #endif - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->setOpt(optname, optval, optlen); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.setOpt(optname, optval, optlen); return 0; } catch (const CUDTException& e) @@ -3889,8 +3881,8 @@ int64_t srt::CUDT::sendfile( { try { - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - return udt->sendfile(ifs, offset, size, block); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + return udt.sendfile(ifs, offset, size, block); } catch (const CUDTException& e) { @@ -4237,8 +4229,8 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta try { - CUDT* udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; - udt->bstats(perf, clear, instantaneous); + CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + udt.bstats(perf, clear, instantaneous); return 0; } catch (const CUDTException& e) @@ -4281,7 +4273,7 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { - return s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->m_pUDT; + return &s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); } catch (const CUDTException& e) { diff --git a/srtcore/api.h b/srtcore/api.h index e1ba244f3..a1bfbffd7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -76,6 +76,8 @@ namespace srt { class CUDT; +/// @brief Class CUDTSocket is a control layer on top of the CUDT core functionality layer. +/// CUDTSocket owns CUDT. class CUDTSocket { public: @@ -89,7 +91,26 @@ class CUDTSocket , m_GroupOf() #endif , m_iISN(0) - , m_pUDT(NULL) + , m_UDT(this) + , m_AcceptCond() + , m_AcceptLock() + , m_uiBackLog(0) + , m_iMuxID(-1) + { + construct(); + } + + CUDTSocket(const CUDTSocket& ancestor) + : m_Status(SRTS_INIT) + , m_SocketID(0) + , m_ListenSocket(0) + , m_PeerID(0) +#if ENABLE_EXPERIMENTAL_BONDING + , m_GroupMemberData() + , m_GroupOf() +#endif + , m_iISN(0) + , m_UDT(this, ancestor.m_UDT) , m_AcceptCond() , m_AcceptLock() , m_uiBackLog(0) @@ -125,8 +146,10 @@ class CUDTSocket int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port - CUDT* m_pUDT; //< pointer to the UDT entity +private: + CUDT m_UDT; //< internal SRT socket logic +public: std::set m_QueuedSockets; //< set of connections waiting for accept() sync::Condition m_AcceptCond; //< used to block "accept" call @@ -149,7 +172,8 @@ class CUDTSocket sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect - CUDT& core() { return *m_pUDT; } + CUDT& core() { return m_UDT; } + const CUDT& core() const { return m_UDT; } static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { @@ -188,7 +212,6 @@ class CUDTSocket bool broken() const; private: - CUDTSocket(const CUDTSocket&); CUDTSocket& operator=(const CUDTSocket&); }; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7c0f3acf5..c887218d9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4670,8 +4670,8 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // the local port must be correctly assigned BEFORE CUDT::startConnect(), // otherwise if startConnect() fails, the multiplexer cannot be located // by garbage collection and will cause leak - s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); - CIPAddress::pton((s->m_SelfAddr), s->m_pUDT->m_piSelfIP, m_PeerAddr); + s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + CIPAddress::pton((s->m_SelfAddr), s->core().m_piSelfIP, m_PeerAddr); //int token = -1; #if ENABLE_EXPERIMENTAL_BONDING @@ -11154,10 +11154,10 @@ void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { CUDTSocket *s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return -1; - CSndBuffer *b = s->m_pUDT->m_pSndBuffer; + CSndBuffer *b = s->core().m_pSndBuffer; if (!b) return -1; @@ -11177,32 +11177,32 @@ int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) int srt::CUDT::rejectReason(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return SRT_REJ_UNKNOWN; - return s->m_pUDT->m_RejectReason; + return s->core().m_RejectReason; } int srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); if (value < SRT_REJC_PREDEFINED) return APIError(MJ_NOTSUP, MN_INVAL); - s->m_pUDT->m_RejectReason = value; + s->core().m_RejectReason = value; return 0; } int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { CUDTSocket* s = s_UDTUnited.locateSocket(u); - if (!s || !s->m_pUDT) + if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); - return count_microseconds(s->m_pUDT->m_stats.tsStartTime.time_since_epoch()); + return count_microseconds(s->core().m_stats.tsStartTime.time_since_epoch()); } bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f2f99201b..2c9bf5f6d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -231,7 +231,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) gli_t end = m_Group.end(); if (m_iMaxPayloadSize == -1) { - int plsize = data.ps->m_pUDT->OPT_PayloadSize(); + int plsize = data.ps->core().OPT_PayloadSize(); HLOGC(gmlog.Debug, log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); @@ -2738,13 +2738,13 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady continue; // Skip the entity that has reported this - if (cu == gi->ps->m_pUDT) + if (cu == &gi->ps->core()) continue; steady_clock::time_point this_timebase; steady_clock::duration this_udrift(0); bool wrp = false; - gi->ps->m_pUDT->m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); + gi->ps->core().m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); udrift = std::min(udrift, this_udrift); steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); @@ -2775,7 +2775,7 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady if (gi->laststatus != SRTS_CONNECTED) continue; - gi->ps->m_pUDT->m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); + gi->ps->core().m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); } } @@ -3094,7 +3094,7 @@ void CUDTGroup::sendBackup_CheckIdleTime(gli_t w_d) // probability that the link will be recognized as IDLE on the // reception side ASAP. int32_t arg = 1; - w_d->ps->m_pUDT->sendCtrl(UMSG_KEEPALIVE, &arg); + w_d->ps->core().sendCtrl(UMSG_KEEPALIVE, &arg); } } @@ -4411,9 +4411,9 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) HLOGC(grlog.Debug, log << "updateLatestRcv: BACKUP group, updating from active link @" << s->m_SocketID << " with %" - << s->m_pUDT->m_iRcvLastSkipAck); + << s->core().m_iRcvLastSkipAck); - CUDT* source = s->m_pUDT; + CUDT* source = &s->core(); vector targets; UniqueLock lg(m_GroupLock); @@ -4448,13 +4448,13 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } // Sanity check - if (!gi->ps->m_pUDT->m_bConnected) + if (!gi->ps->core().m_bConnected) { HLOGC(grlog.Debug, log << "grp: IPE: NOT updating rcv-seq on @" << gi->id << " - IDLE BUT NOT CONNECTED"); continue; } - targets.push_back(gi->ps->m_pUDT); + targets.push_back(&gi->ps->core()); } lg.unlock(); From 399e8bf69fde85bb107d30babf21402cf6789a2e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 10:53:12 +0200 Subject: [PATCH 134/683] [tests] Test FileUpload: find unused UDP port (#2086) --- test/test_file_transmission.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 790555eb7..8150ca3f2 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -45,17 +45,35 @@ TEST(Transmission, FileUpload) sa_lsn.sin_addr.s_addr = INADDR_ANY; sa_lsn.sin_port = htons(5555); + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (int port = 5000; port <= 5555; ++port) + { + sa_lsn.sin_port = htons(port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << port << "\n"; + break; + } + + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); + srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); int optval = 0; int optlen = sizeof optval; ASSERT_EQ(srt_getsockflag(sock_lsn, SRTO_SNDBUF, &optval, &optlen), 0); - size_t filesize = 7 * optval; + const size_t filesize = 7 * optval; { std::cout << "WILL CREATE source file with size=" << filesize << " (= 7 * " << optval << "[sndbuf])\n"; std::ofstream outfile("file.source", std::ios::out | std::ios::binary); - ASSERT_EQ(!!outfile, true); + ASSERT_EQ(!!outfile, true) << srt_getlasterror_str(); srand(time(0)); @@ -96,6 +114,7 @@ TEST(Transmission, FileUpload) ASSERT_NE(n, SRT_ERROR); if (n == 0) { + std::cerr << "Received 0 bytes, breaking.\n"; break; } @@ -110,7 +129,7 @@ TEST(Transmission, FileUpload) sockaddr_in sa = sockaddr_in(); sa.sin_family = AF_INET; - sa.sin_port = htons(5555); + sa.sin_port = sa_lsn.sin_port; ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); srt_connect(sock_clr, (sockaddr*)&sa, sizeof(sa)); @@ -126,8 +145,8 @@ TEST(Transmission, FileUpload) size_t shift = 0; while (n > 0) { - int st = srt_send(sock_clr, buf.data()+shift, n); - ASSERT_GT(st, 0); + const int st = srt_send(sock_clr, buf.data()+shift, n); + ASSERT_GT(st, 0) << srt_getlasterror_str(); n -= st; shift += st; From 0925d6891a9f1226de547293c3dd614f4df209cb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 15:38:18 +0200 Subject: [PATCH 135/683] [docs] Improved a note about srt_recv(..) and payload size (#2080) --- docs/API/API-functions.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index d59ce481e..58b729fad 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1885,8 +1885,13 @@ to be returned does not fit in the buffer, nothing will be received and the error is reported. 3. In **live mode**, the function behaves as in **file/message mode**, although the -number of bytes retrieved will be at most the size of `SRTO_PAYLOADSIZE`. In this mode, -however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) +number of bytes retrieved will be at most the maximum payload of one MTU. +The [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) value configured by the sender +is not negotiated, and not known to the receiver. +The [`SRTO_PAYLOADSIZE`](API-socket-options.md#SRTO_PAYLOADSIZE) value set on the SRT receiver +is mainly used for heuristics. However, the receiver is prepared to receive +the whole MTU as configured with [`SRTO_MSS`](API-socket-options.md#SRTO_MSS). +In this mode, however, with default settings of [`SRTO_TSBPDMODE`](API-socket-options.md#SRTO_TSBPDMODE) and [`SRTO_TLPKTDROP`](API-socket-options.md#SRTO_TLPKTDROP), the message will be received only when its time to play has come, and until then it will be kept in the receiver buffer. Also, when the time to play has come for a message that is next to From 7e2d82bab90fc1dfddd312ebeddb153cc186ace3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 09:59:27 +0200 Subject: [PATCH 136/683] [apps] Fixed loop over OptionScheme (#2089) --- apps/apputil.cpp | 2 +- apps/apputil.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index bf808a95d..611b99976 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -251,7 +251,7 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector } // Find the key in the scheme. If not found, treat it as ARG_NONE. - for (auto s: scheme) + for (const auto& s: scheme) { if (s.names().count(current_key)) { diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 4f2b84bf7..5bac461e0 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -220,7 +220,7 @@ struct OptionScheme OptionScheme(const OptionName& id, Args tp); - const std::set& names(); + const std::set& names() const; }; struct OptionName @@ -265,7 +265,7 @@ struct OptionName }; inline OptionScheme::OptionScheme(const OptionName& id, Args tp): pid(&id), type(tp) {} -inline const std::set& OptionScheme::names() { return pid->names; } +inline const std::set& OptionScheme::names() const { return pid->names; } template inline typename OutType::type Option(const options_t&, OutValue deflt=OutValue()) { return deflt; } From aed5f2086d2029b0b323687f6d6377ffa54ccd2f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 12:04:27 +0200 Subject: [PATCH 137/683] [build] Default STDCXX_SYNC=ON on Windows, try to use CLOCK_MONOTONIC by default on Linux. (#2088) Updated Windows build instructions (default C++11). --- CMakeLists.txt | 27 ++++++++------ docs/build/build-win.md | 16 ++++++--- scripts/haiUtil.cmake | 79 ++++++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f210ff34f..c1cb13ced 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,16 @@ if (ENABLE_DEBUG) endif() +set(ENABLE_STDCXX_SYNC_DEFAULT OFF) +set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF) +set(MONOTONIC_CLOCK_LINKLIB "") +if (MICROSOFT) + set(ENABLE_STDCXX_SYNC_DEFAULT ON) +elseif (LINUX) + test_requires_clock_gettime(ENABLE_MONOTONIC_CLOCK_DEFAULT MONOTONIC_CLOCK_LINKLIB) +endif() + + # options option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) @@ -129,8 +139,8 @@ option(ENABLE_CXX_DEPS "Extra library dependencies in srt.pc for the CXX librari option(USE_STATIC_LIBSTDCXX "Should use static rather than shared libstdc++" OFF) option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON) option(ENABLE_CODE_COVERAGE "Enable code coverage reporting" OFF) -option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" OFF) -option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" OFF) +option(ENABLE_MONOTONIC_CLOCK "Enforced clock_gettime with monotonic clock on GC CV" ${ENABLE_MONOTONIC_CLOCK_DEFAULT}) +option(ENABLE_STDCXX_SYNC "Use C++11 chrono and threads for timing instead of pthreads" ${ENABLE_STDCXX_SYNC_DEFAULT}) option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) @@ -265,6 +275,10 @@ if (DEFINED HAVE_INET_PTON) endif() if (ENABLE_MONOTONIC_CLOCK) + if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) + message(FATAL_ERROR "Your platform does not support CLOCK_MONOTONIC. Build with -DENABLE_MONOTONIC_CLOCK=OFF.") + endif() + set (WITH_EXTRALIBS "${WITH_EXTRALIBS} ${MONOTONIC_CLOCK_LINKLIB}") add_definitions(-DENABLE_MONOTONIC_CLOCK=1) endif() @@ -435,6 +449,7 @@ elseif (ENABLE_STDCXX_SYNC) endif() message(STATUS "STDCXX_SYNC: ${ENABLE_STDCXX_SYNC}") +message(STATUS "MONOTONIC_CLOCK: ${ENABLE_MONOTONIC_CLOCK}") if (ENABLE_SOCK_CLOEXEC) add_definitions(-DENABLE_SOCK_CLOEXEC=1) @@ -526,14 +541,6 @@ if (USE_STATIC_LIBSTDCXX) endif() endif() -# We need clock_gettime, but on some systems this is only provided -# by librt. Check if librt is required. -if (ENABLE_MONOTONIC_CLOCK AND LINUX) - # "requires" - exits on FATAL_ERROR when clock_gettime not available - test_requires_clock_gettime(NEED_CLOCK_GETTIME) - set (WITH_EXTRALIBS "${WITH_EXTRALIBS} ${NEED_CLOCK_GETTIME}") -endif() - # This options is necessary on some systems; on a cross-ARM compiler it # has been detected, for example, that -lrt is necessary for some applications diff --git a/docs/build/build-win.md b/docs/build/build-win.md index 3f47b310b..e09175142 100644 --- a/docs/build/build-win.md +++ b/docs/build/build-win.md @@ -54,7 +54,7 @@ only unencrypted mode can be used. With the enabled SRT encryption, one of the following Crypto libraries is required: -- `OpenSSL` (default) +- `OpenSSL` (**default**) - `LibreSSL` - `MbedTLS` @@ -62,8 +62,8 @@ one of the following Crypto libraries is required: SRT as of v1.4.2 supports two threading libraries: -- `pthreads` (default) -- Standard C++ thread library available in C++11 (recommended for Windows) +- Standard C++ thread library available in C++11 (**default on Windows**) +- `pthreads` (not recommended on Windows) The `pthreads` library is provided out-of-the-box on all POSIX-based systems. On Windows it can be provided as a 3rd party library (see below). @@ -73,6 +73,10 @@ However the C++ standard thread library is recommended to be used on Windows. #### 1.3.1. VCpkg Packet Manager (optional) +Can be used to: +- build OpenSSL library (dependency of SRT). +- build pthreads library (dependency of SRT). + [vcpkg](https://github.com/microsoft/vcpkg) is a C++ library manager for Windows, Linux and MacOS. Consider its [prerequisites](https://github.com/microsoft/vcpkg/blob/master/README.md#quick-start) before proceeding. @@ -181,8 +185,8 @@ to specify the directory that will contain the LibreSSL headers and libraries. SRT can use one of these two threading libraries: -- C++11 threads (SRT v1.4.2 and above) - recommended for Windows -- `pthreads` (default) +- C++11 threads (SRT v1.4.2 and above) - recommended, default since SRT v1.4.4; +- `pthreads` (not recommended on Windows). #### 2.2.1. Using C++11 Threading @@ -193,6 +197,8 @@ Otherwise the external PThreads for Windows wrapper library is required. #### 2.2.2. Building PThreads +It is not recommended to use `pthreads` port on Windows. Consider using [C++11 instead](#221-using-c11-threading), + ##### 2.2.2.1. Using vcpkg **Note!** The `vcpkg` working directory is referenced as `VCPKG_ROOT`. diff --git a/scripts/haiUtil.cmake b/scripts/haiUtil.cmake index e161f1e5b..9e4fb4d56 100644 --- a/scripts/haiUtil.cmake +++ b/scripts/haiUtil.cmake @@ -12,11 +12,11 @@ include(CheckCXXSourceCompiles) # Useful for combinging paths function(adddirname prefix lst out_lst) - set(output) - foreach(item ${lst}) - list(APPEND output "${prefix}/${item}") - endforeach() - set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE) + set(output) + foreach(item ${lst}) + list(APPEND output "${prefix}/${item}") + endforeach() + set(${out_lst} ${${out_lst}} ${output} PARENT_SCOPE) endfunction() # Splits a version formed as "major.minor.patch" recorded in variable 'prefix' @@ -32,11 +32,11 @@ ENDMACRO(set_version_variables) # Sets given variable to 1, if the condition that follows it is satisfied. # Otherwise set it to 0. MACRO(set_if varname) - IF(${ARGN}) - SET(${varname} 1) - ELSE(${ARGN}) - SET(${varname} 0) - ENDIF(${ARGN}) + IF(${ARGN}) + SET(${varname} 1) + ELSE(${ARGN}) + SET(${varname} 0) + ENDIF(${ARGN}) ENDMACRO(set_if) FUNCTION(join_arguments outvar) @@ -80,11 +80,11 @@ MACRO(MafReadDir directory maffile) configure_file(${directory}/${maffile} dummy_${maffile}.cmake.out) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/dummy_${maffile}.cmake.out) - #message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}") - #message("DEBUG: PASSED VARIABLES:") - #foreach(DEBUG_VAR ${MAFREAD_TAGS}) - # message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}") - #endforeach() + #message("DEBUG: MAF FILE CONTENTS: ${MAFREAD_CONTENTS}") + #message("DEBUG: PASSED VARIABLES:") + #foreach(DEBUG_VAR ${MAFREAD_TAGS}) + # message("DEBUG: ${DEBUG_VAR}=${MAFREAD_VAR_${DEBUG_VAR}}") + #endforeach() # The unnamed section becomes SOURCES set (MAFREAD_VARIABLE ${MAFREAD_VAR_SOURCES}) @@ -186,15 +186,15 @@ MACRO(MafReadDir directory maffile) ENDFOREACH() # Final debug report - #set (ALL_VARS "") - #message("DEBUG: extracted variables:") - #foreach(DEBUG_VAR ${MAFREAD_TAGS}) - # list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}}) - #endforeach() - #list(REMOVE_DUPLICATES ALL_VARS) - #foreach(DEBUG_VAR ${ALL_VARS}) - # message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}") - #endforeach() + #set (ALL_VARS "") + #message("DEBUG: extracted variables:") + #foreach(DEBUG_VAR ${MAFREAD_TAGS}) + # list(APPEND ALL_VARS ${MAFREAD_VAR_${DEBUG_VAR}}) + #endforeach() + #list(REMOVE_DUPLICATES ALL_VARS) + #foreach(DEBUG_VAR ${ALL_VARS}) + # message("DEBUG: --> ${DEBUG_VAR} = ${${DEBUG_VAR}}") + #endforeach() ENDMACRO(MafReadDir) # NOTE: This is historical only. Not in use. @@ -214,9 +214,9 @@ MACRO(GetMafHeaders directory outvar) ENDMACRO(GetMafHeaders) function (getVarsWith _prefix _varResult) - get_cmake_property(_vars VARIABLES) - string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}") - set (${_varResult} ${_matchedVars} PARENT_SCOPE) + get_cmake_property(_vars VARIABLES) + string (REGEX MATCHALL "(^|;)${_prefix}[A-Za-z0-9_]*" _matchedVars "${_vars}") + set (${_varResult} ${_matchedVars} PARENT_SCOPE) endfunction() function (check_testcode_compiles testcode libraries _successful) @@ -228,15 +228,18 @@ function (check_testcode_compiles testcode libraries _successful) set (CMAKE_REQUIRED_LIBRARIES ${save_required_libraries}) endfunction() -function (test_requires_clock_gettime _result) +function (test_requires_clock_gettime _enable _linklib) # This function tests if clock_gettime can be used # - at all # - with or without librt # Result will be: - # rt (if librt required) - # "" (if no extra libraries required) - # -- killed by FATAL_ERROR if clock_gettime is not available + # - CLOCK_MONOTONIC is available, link with librt: + # _enable = ON; _linklib = "-lrt". + # - CLOCK_MONOTONIC is available, link without librt: + # _enable = ON; _linklib = "". + # - CLOCK_MONOTONIC is not available: + # _enable = OFF; _linklib = "-". set (code " #include @@ -249,19 +252,23 @@ function (test_requires_clock_gettime _result) check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN) if (HAVE_CLOCK_GETTIME_IN) - message(STATUS "Checked clock_gettime(): no extra libs needed") - set (${_result} "" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: availabe, no extra libs needed") + set (${_enable} ON PARENT_SCOPE) + set (${_linklib} "" PARENT_SCOPE) return() endif() check_testcode_compiles(${code} "rt" HAVE_CLOCK_GETTIME_LIBRT) if (HAVE_CLOCK_GETTIME_LIBRT) - message(STATUS "Checked clock_gettime(): requires -lrt") - set (${_result} "-lrt" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: available, requires -lrt") + set (${_enable} ON PARENT_SCOPE) + set (${_linklib} "-lrt" PARENT_SCOPE) return() endif() - message(FATAL_ERROR "clock_gettime() is not available on this system") + set (${_enable} OFF PARENT_SCOPE) + set (${_linklib} "-" PARENT_SCOPE) + message(STATUS "CLOCK_MONOTONIC: not available on this system") endfunction() function (parse_compiler_type wct _type _suffix) From 9475ad0ec04cd67cf7b43ddd9a53230ee09df769 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 11:08:52 +0200 Subject: [PATCH 138/683] [core] Removed redundant srt namespace scope resolution in CUDT --- srtcore/core.h | 90 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/srtcore/core.h b/srtcore/core.h index 6b8472c64..9c9a38f04 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -172,8 +172,8 @@ class CUDT typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; - typedef srt::sync::AtomicClock atomic_time_point; - typedef srt::sync::AtomicDuration atomic_duration; + typedef sync::AtomicClock atomic_time_point; + typedef sync::AtomicDuration atomic_duration; private: // constructor and desctructor void construct(); @@ -313,8 +313,8 @@ class CUDT int32_t schedSeqNo() const { return m_iSndNextSeqNo; } bool overrideSndSeqNo(int32_t seq); - srt::sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } - srt::sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } + sync::steady_clock::time_point lastRspTime() const { return m_tsLastRspTime.load(); } + sync::steady_clock::time_point freshActivationStart() const { return m_tsFreshActivation; } int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } int flowWindowSize() const { return m_iFlowWindowSize; } @@ -386,7 +386,7 @@ class CUDT // So, this can be simply defined as: TS = (RTS - STS) % (MAX_TIMESTAMP+1) // XXX Would be nice to check if local_time > m_tsStartTime, // otherwise it may go unnoticed with clock skew. - return (int32_t) srt::sync::count_microseconds(from_time - m_stats.tsStartTime); + return (int32_t) sync::count_microseconds(from_time - m_stats.tsStartTime); } void setPacketTS(CPacket& p, const time_point& local_time) @@ -398,14 +398,14 @@ class CUDT // immediately to free the socket void notListening() { - srt::sync::ScopedLock cg(m_ConnectionLock); + sync::ScopedLock cg(m_ConnectionLock); m_bListening = false; m_pRcvQueue->removeListener(this); } static int32_t generateISN() { - using namespace srt::sync; + using namespace sync; return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } @@ -420,8 +420,8 @@ class CUDT SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); - SRTU_PROPERTY_RR(srt::sync::Condition*, recvDataCond, &m_RecvDataCond); - SRTU_PROPERTY_RR(srt::sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); + SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond); + SRTU_PROPERTY_RR(sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); /// @brief Request a socket to be broken due to too long instability (normally by a group). void breakAsUnstable() { m_bBreakAsUnstable = true; } @@ -734,30 +734,30 @@ class CUDT void EmitSignal(ETransmissionEvent tev, EventVariant var); // Internal state - srt::sync::atomic m_bListening; // If the UDT entity is listening to connection - srt::sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed - srt::sync::atomic m_bConnected; // Whether the connection is on or off - srt::sync::atomic m_bClosing; // If the UDT entity is closing - srt::sync::atomic m_bShutdown; // If the peer side has shutdown the connection - srt::sync::atomic m_bBroken; // If the connection has been broken - srt::sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. - srt::sync::atomic m_bPeerHealth; // If the peer status is normal - srt::sync::atomic m_RejectReason; + sync::atomic m_bListening; // If the UDT entity is listening to connection + sync::atomic m_bConnecting; // The short phase when connect() is called but not yet completed + sync::atomic m_bConnected; // Whether the connection is on or off + sync::atomic m_bClosing; // If the UDT entity is closing + sync::atomic m_bShutdown; // If the peer side has shutdown the connection + sync::atomic m_bBroken; // If the connection has been broken + sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. + sync::atomic m_bPeerHealth; // If the peer status is normal + sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - srt::sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter - srt::sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second - srt::sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) + sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second + sync::atomic m_iSRTT; // Smoothed RTT (an exponentially-weighted moving average (EWMA) // of an endpoint's RTT samples), in microseconds - srt::sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds - srt::sync::atomic m_bIsFirstRTTReceived;// True if the first RTT sample was obtained from the ACK/ACKACK pair + sync::atomic m_iRTTVar; // The variation in the RTT samples (RTT variance), in microseconds + sync::atomic m_bIsFirstRTTReceived; // True if the first RTT sample was obtained from the ACK/ACKACK pair // at the receiver side or received by the sender from an ACK packet. // It's used to reset the initial value of smoothed RTT (m_iSRTT) // at the beginning of transmission (including the one taken from // cache). False by default. - srt::sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side - srt::sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side + sync::atomic m_iDeliveryRate; // Packet arrival rate at the receiver side + sync::atomic m_iByteDeliveryRate; // Byte arrival rate at the receiver side CHandShake m_ConnReq; // Connection request CHandShake m_ConnRes; // Connection response @@ -773,7 +773,7 @@ class CUDT atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time - srt::sync::atomic m_iFlowWindowSize; // Flow control window size + sync::atomic m_iFlowWindowSize; // Flow control window size double m_dCongestionWindow; // Congestion window size private: // Timers @@ -798,8 +798,8 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending - srt::sync::atomic m_iSndLastFullAck;// Last full ACK received - srt::sync::atomic m_iSndLastAck; // Last ACK received + sync::atomic m_iSndLastFullAck; // Last full ACK received + sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) // and this is the sequence number that refers to the block at position [0]. Upon acknowledgement, @@ -809,9 +809,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - srt::sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list - srt::sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - srt::sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list + sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -873,7 +873,7 @@ class CUDT int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged int32_t m_iAckSeqNo; // Last ACK sequence number - srt::sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number + sync::atomic m_iRcvCurrSeqNo; // Largest received sequence number int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter) int32_t m_iPeerISN; // Initial Sequence Number of the peer side @@ -884,10 +884,10 @@ class CUDT bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead - srt::sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle - srt::sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock + sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle + sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent - srt::sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining + sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; CallbackHolder m_cbConnectHook; @@ -909,21 +909,21 @@ class CUDT private: // synchronization: mutexes and conditions - srt::sync::Mutex m_ConnectionLock; // used to synchronize connection operation + sync::Mutex m_ConnectionLock; // used to synchronize connection operation - srt::sync::Condition m_SendBlockCond; // used to block "send" call - srt::sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond + sync::Condition m_SendBlockCond; // used to block "send" call + sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond - srt::sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer + sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer // Protects access to m_iSndCurrSeqNo, m_iSndLastAck - srt::sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) + sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) - srt::sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock - srt::sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady()) + sync::Condition m_RecvDataCond; // used to block "srt_recv*" when there is no data. Use together with m_RecvLock + sync::Mutex m_RecvLock; // used to synchronize "srt_recv*" call, protects TSBPD drift updates (CRcvBuffer::isRcvDataReady()) - srt::sync::Mutex m_SendLock; // used to synchronize "send" call - srt::sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) - mutable srt::sync::Mutex m_StatsLock; // used to synchronize access to trace statistics + sync::Mutex m_SendLock; // used to synchronize "send" call + sync::Mutex m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd) + mutable sync::Mutex m_StatsLock; // used to synchronize access to trace statistics void initSynch(); void destroySynch(); From 675e75de015f61c156a1c9fa50a3e8607d721e5a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 11:12:21 +0200 Subject: [PATCH 139/683] [core] Added SRT_ATTR_GUARDED_BY to some members of CUDT. Imported from #1859. Co-authored-by: Mikolaj Malecki --- srtcore/core.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/srtcore/core.h b/srtcore/core.h index 9c9a38f04..a9d2f9dc3 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -744,7 +744,7 @@ class CUDT sync::atomic m_bPeerHealth; // If the peer status is normal sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected int m_iEXPCount; // Expiration counter sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second @@ -773,6 +773,7 @@ class CUDT atomic_duration m_tdSendTimeDiff; // Aggregate difference in inter-packet sending time + SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iFlowWindowSize; // Flow control window size double m_dCongestionWindow; // Congestion window size @@ -782,6 +783,7 @@ class CUDT duration m_tdACKInterval; // ACK interval duration m_tdNAKInterval; // NAK interval + SRT_ATTR_GUARDED_BY(m_RecvAckLock) atomic_time_point m_tsLastRspTime; // Timestamp of last response from the peer time_point m_tsLastRspAckTime; // Timestamp of last ACK from the peer atomic_time_point m_tsLastSndTime; // Timestamp of last data/ctrl sent (in system ticks) @@ -799,6 +801,7 @@ class CUDT time_point m_tsNextSendTime; // Scheduled time of next packet sending sync::atomic m_iSndLastFullAck; // Last full ACK received + SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iSndLastAck; // Last ACK received // NOTE: m_iSndLastDataAck is the value strictly bound to the CSndBufer object (m_pSndBuffer) @@ -809,9 +812,9 @@ class CUDT // to the sending buffer. This way, extraction of an old packet for retransmission should // require only the lost sequence number, and how to find the packet with this sequence // will be up to the sending buffer. - sync::atomic m_iSndLastDataAck;// The real last ACK that updates the sender buffer and loss list - sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT - sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet + sync::atomic m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list + sync::atomic m_iSndCurrSeqNo; // The largest sequence number that HAS BEEN SENT + sync::atomic m_iSndNextSeqNo; // The sequence number predicted to be placed at the currently scheduled packet // Note important differences between Curr and Next fields: // - m_iSndCurrSeqNo: this is used by SRT:SndQ:worker thread and it's operated from CUDT::packData @@ -853,6 +856,8 @@ class CUDT bool m_bPeerTLPktDrop; // Enable sender late packet dropping bool m_bPeerNakReport; // Sender's peer (receiver) issues Periodic NAK Reports bool m_bPeerRexmitFlag; // Receiver supports rexmit flag in payload packets + + SRT_ATTR_GUARDED_BY(m_RecvAckLock) int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data From 4fe19fcd2718602bab508a7cc5ccd57e4dd3ccd0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 11:18:38 +0200 Subject: [PATCH 140/683] [docs] Updated RCV buffer size calculation guide. Using long long for high bitrates. --- docs/API/configuration-guidelines.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/API/configuration-guidelines.md b/docs/API/configuration-guidelines.md index 54d3cd15a..83693d1f1 100644 --- a/docs/API/configuration-guidelines.md +++ b/docs/API/configuration-guidelines.md @@ -87,13 +87,12 @@ where ```c++ - auto CalculateTargetRBufSize(int msRTT, int bpsRate, int bytesPayloadSize, int msLatency, int SRTO_MSS) { const int UDPHDR_SIZE = 28; - const int targetPayloadBytes = (msLatency + msRTT / 2) * bpsRate / 1000 / 8; - const int targetNumPackets = targetPayloadBytes / bytesPayloadSize; - const int targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); + const long long targetPayloadBytes = static_cast(msLatency + msRTT / 2) * bpsRate / 1000 / 8; + const long long targetNumPackets = targetPayloadBytes / bytesPayloadSize; + const long long targetSizeValue = targetNumPackets * (SRTO_MSS - UDPHDR_SIZE); return {targetNumPackets, targetSizeValue}; } From 67f70d0e6039ef0cf795e7b9f61b523dab6f17ce Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 17 Aug 2021 15:46:31 +0200 Subject: [PATCH 141/683] [apps] Fixed testing apps building with GCC 4.8 --- testing/testactivemedia.hpp | 2 +- testing/testmediabase.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index b94ff9aa3..06a1cd922 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -29,7 +29,7 @@ struct Medium std::mutex buffer_lock; std::thread thr; std::condition_variable ready; - std::atomic running {false}; + std::atomic running = {false}; std::exception_ptr xp; // To catch exception thrown by a thread virtual void Runner() = 0; diff --git a/testing/testmediabase.hpp b/testing/testmediabase.hpp index 3eb16a4bb..04a85d435 100644 --- a/testing/testmediabase.hpp +++ b/testing/testmediabase.hpp @@ -11,6 +11,7 @@ #ifndef INC_SRT_COMMON_TRANMITBASE_HPP #define INC_SRT_COMMON_TRANMITBASE_HPP +#include #include #include #include From 5f37067ce077b417bafb4f4e7a0766a3f794efe8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 11:38:23 +0200 Subject: [PATCH 142/683] [apps] Workaround bad move semantics of OptionScheme. OptionScheme::pid does not own an object. --- testing/srt-test-multiplex.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 2b6b2b2af..859547069 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -464,11 +464,15 @@ int main( int argc, char** argv ) } } cleanupobj; - // Check options + const OptionName + o_loglevel = { "ll", "loglevel" }, + o_input = { "i" }, + o_output = { "o" }; + vector optargs = { - { {"ll", "loglevel"}, OptionScheme::ARG_ONE }, - { {"i"}, OptionScheme::ARG_VAR }, - { {"o"}, OptionScheme::ARG_VAR } + { o_loglevel, OptionScheme::ARG_ONE }, + { o_input, OptionScheme::ARG_VAR }, + { o_output, OptionScheme::ARG_VAR } }; map> params = ProcessOptions(argv, argc, optargs); From 74aff8287caf404cdcb7b33172412709a1a20821 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 18 Aug 2021 12:44:40 +0200 Subject: [PATCH 143/683] [build] Bump version to 1.4.4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cb13ced..a52dbad49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.4.3) +set (SRT_VERSION 1.4.4) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables From 8b1be6196f109f2fb8a1a791bfb092e9ca41af36 Mon Sep 17 00:00:00 2001 From: ThibaultBee <37510686+ThibaultBee@users.noreply.github.com> Date: Mon, 23 Aug 2021 12:15:40 +0200 Subject: [PATCH 144/683] [build] Fixed ENABLE_MONOTONIC_CLOCK=ON when target is Android --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a52dbad49..242bf7e70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,8 @@ set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(POSIX LINUX OR DARWIN OR BSD OR (CYGWIN AND CYGWIN_USE_POSIX)) +set_if(ANDROID ${CMAKE_SYSTEM_NAME} MATCHES "ANDROID") +set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) # Not sure what to do in case of compiling by MSVC. @@ -114,7 +115,7 @@ set(ENABLE_MONOTONIC_CLOCK_DEFAULT OFF) set(MONOTONIC_CLOCK_LINKLIB "") if (MICROSOFT) set(ENABLE_STDCXX_SYNC_DEFAULT ON) -elseif (LINUX) +elseif (POSIX) test_requires_clock_gettime(ENABLE_MONOTONIC_CLOCK_DEFAULT MONOTONIC_CLOCK_LINKLIB) endif() From cbdd676aa50d4517eb634f54ece27f93894f4f0d Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Sat, 21 Aug 2021 15:50:53 +0800 Subject: [PATCH 145/683] [core] Added missing lock for isRcvDataReady() --- srtcore/group.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 2c9bf5f6d..e835453ea 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2071,11 +2071,13 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& readReady.push_back(*sockiter); } - else if (sock->core().m_pRcvBuffer->isRcvDataReady()) + else { // No read-readiness reported by epoll, but probably missed or not yet handled // as the receiver buffer is read-ready. - readReady.push_back(sock); + ScopedLock lg(sock->core().m_RcvBufferLock); + if (sock->core().m_pRcvBuffer && sock->core().m_pRcvBuffer->isRcvDataReady()) + readReady.push_back(sock); } } From ab9e69b28d2193e4f407537e2c144fdae09628a0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 25 Aug 2021 10:45:22 +0200 Subject: [PATCH 146/683] [docs] Removed a reference to YAML syntax as it is not used actually. See #2047. --- docs/features/access-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/access-control.md b/docs/features/access-control.md index 101502566..d583f795b 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -46,7 +46,7 @@ specification in POSIX: `#!`. The next two characters are: -- `:` - this marks the YAML format, the only one currently used +- `:` - marks the format of the following key-value pair syntax (the only one defined currently). - The content format, which is either: - `:` - the comma-separated keys with no nesting - `{` - like above, but nesting is allowed and must end with `}` From da706240d10332ae9bb804558823996963f7160d Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 25 Aug 2021 03:46:55 -0500 Subject: [PATCH 147/683] [build] Fix Build for Android. (#2100) Some versions of CMake set CMAKE_SYSTEM_NAME to "Android". Link the atomic library for Android targets. --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 242bf7e70..4d817261a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(ANDROID ${CMAKE_SYSTEM_NAME} MATCHES "ANDROID") +set_if(ANDROID "${CMAKE_SYSTEM_NAME}" MATCHES "^[Aa][Nn][Dd][Rr][Oo][Ii][Dd]") set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) @@ -867,6 +867,8 @@ if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) + elseif (ANDROID) + target_link_libraries(${TARGET_srt}_shared PRIVATE atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_shared PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -901,6 +903,8 @@ if (srt_libspec_static) endif() elseif (MINGW) target_link_libraries(${TARGET_srt}_static PRIVATE wsock32 ws2_32) + elseif (ANDROID) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_static PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) From fb0987500f1fbc33ef156c13768646adcd7af7be Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Fri, 27 Aug 2021 04:51:42 -0500 Subject: [PATCH 148/683] [build] Fix build for Linux GLIBC-2.8 and earlier. (#2103) --- srtcore/epoll.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index bfce672cc..d4919ff39 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -112,11 +112,31 @@ int CEPoll::create(CEPollDesc** pout) int localid = 0; #ifdef LINUX - int flags = 0; -#if ENABLE_SOCK_CLOEXEC - flags |= EPOLL_CLOEXEC; -#endif - localid = epoll_create1(flags); + + // NOTE: epoll_create1() and EPOLL_CLOEXEC were introduced in GLIBC-2.9. + // So earlier versions of GLIBC, must use epoll_create() and set + // FD_CLOEXEC on the file descriptor returned by it after the fact. + #if defined(EPOLL_CLOEXEC) + int flags = 0; + #if ENABLE_SOCK_CLOEXEC + flags |= EPOLL_CLOEXEC; + #endif + localid = epoll_create1(flags); + #else + localid = epoll_create(1); + #if ENABLE_SOCK_CLOEXEC + if (localid != -1) + { + int fdFlags = fcntl(localid, F_GETFD); + if (fdFlags != -1) + { + fdFlags |= FD_CLOEXEC; + fcntl(localid, F_SETFD, fdFlags); + } + } + #endif + #endif + /* Possible reasons of -1 error: EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered. ENFILE: The system limit on the total number of open files has been reached. From 4fc0f317b04196b9f36c5ac46ba4f357295baa50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 30 Aug 2021 16:18:02 +0200 Subject: [PATCH 149/683] [build] Use lowercase CMAKE_SYSTEM_NAME on Android --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d817261a..ed2253b05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") -set_if(ANDROID "${CMAKE_SYSTEM_NAME}" MATCHES "^[Aa][Nn][Dd][Rr][Oo][Ii][Dd]") +set_if(ANDROID ${SYSNAME_LC} MATCHES "android") set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) From 409a40d408090c56026413e5706414f1363aa430 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Tue, 31 Aug 2021 12:02:28 +0200 Subject: [PATCH 150/683] [core] Detect pthread_getname* availability --- CMakeLists.txt | 3 ++ scripts/FindPThreadGetSetName.cmake | 82 +++++++++++++++++++++++++++++ srtcore/srt_attr_defs.h | 2 +- srtcore/threadname.h | 57 ++++++++++++++++---- 4 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 scripts/FindPThreadGetSetName.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ed2253b05..3693b5b37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,9 @@ if (DEFINED HAVE_INET_PTON) add_definitions(-DHAVE_INET_PTON=1) endif() +include(FindPThreadGetSetName) +FindPThreadGetSetName() # Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* + if (ENABLE_MONOTONIC_CLOCK) if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) message(FATAL_ERROR "Your platform does not support CLOCK_MONOTONIC. Build with -DENABLE_MONOTONIC_CLOCK=OFF.") diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake new file mode 100644 index 000000000..fa818fd73 --- /dev/null +++ b/scripts/FindPThreadGetSetName.cmake @@ -0,0 +1,82 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for pthread_getname_np(3) and pthread_setname_np(3) +# used in srtcore/threadname.h. +# +# Some BSD distros need to include for pthread_getname_np(). +# +# TODO: Some BSD distros have pthread_get_name_np() and pthread_set_name_np() +# instead of pthread_getname_np() and pthread_setname_np(). +# +# Sets: +# HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H +# HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H +# HAVE_PTHREAD_GETNAME_NP +# HAVE_PTHREAD_SETNAME_NP +# Sets as appropriate: +# add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) +# add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) +# add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) +# add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + +include(CheckSymbolExists) + +function(FindPThreadGetSetName) + + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + + unset(HAVE_PTHREAD_GETNAME_NP) + unset(HAVE_PTHREAD_GETNAME_NP PARENT_SCOPE) + unset(HAVE_PTHREAD_GETNAME_NP CACHE) + unset(HAVE_PTHREAD_SETNAME_NP) + unset(HAVE_PTHREAD_SETNAME_NP PARENT_SCOPE) + unset(HAVE_PTHREAD_SETNAME_NP CACHE) + + set(CMAKE_REQUIRED_DEFINITIONS + -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) + set(CMAKE_REQUIRED_FLAGS "-pthread") + + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") + check_symbol_exists( + pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + check_symbol_exists( + pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") + check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + endif() + check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + endif() + if (HAVE_PTHREAD_GETNAME_NP) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) + endif() + if (HAVE_PTHREAD_SETNAME_NP) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + endif() + + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_FLAGS) + +endfunction(FindPThreadGetSetName) diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 82e6e5e3a..00d35e069 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -116,7 +116,7 @@ used by SRT library internally. #define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS #else -#if defined(__clang__) +#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5) #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 2b3964276..e392f6d8e 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -16,24 +16,60 @@ written by #ifndef INC_SRT_THREADNAME_H #define INC_SRT_THREADNAME_H -#if defined(__APPLE__) || defined(__linux__) -#if defined(__linux__) -#include +// NOTE: +// HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H +// HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H +// HAVE_PTHREAD_GETNAME_NP +// HAVE_PTHREAD_GETNAME_NP +// Are detected and set in ../CMakeLists.txt. +// OS Availability of pthread_getname_np(..) and pthread_setname_np(..):: +// MacOS(10.6) +// iOS(3.2) +// AIX(7.1) +// FreeBSD(version?), OpenBSD(Version?) +// Linux-GLIBC(GLIBC-2.12). +// Linux-MUSL(MUSL-1.1.20 Partial Implementation. See below). +// MINGW-W64(4.0.6) + +#if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ + || defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + #include + #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ + && !defined(HAVE_PTHREAD_GETNAME_NP) + #define HAVE_PTHREAD_GETNAME_NP 1 + #endif + #if defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) \ + && !defined(HAVE_PTHREAD_SETNAME_NP) + #define HAVE_PTHREAD_SETNAME_NP 1 + #endif #endif -#include +#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ + || defined(__linux__) + // NOTE: + // Linux pthread_getname_np() and pthread_setname_np() became available + // in GLIBC-2.12 and later. + // Some Linux runtimes do not have pthread_getname_np(), but have + // pthread_setname_np(), for instance MUSL at least as of v1.1.20. + // So using the prctl() for Linux is more portable. + #if defined(__linux__) + #include + #endif + #include #endif #include #include #include +#include "common.h" #include "sync.h" class ThreadName { -#if defined(__APPLE__) || defined(__linux__) +#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ + || defined(__linux__) class ThreadNameImpl { @@ -47,8 +83,7 @@ class ThreadName // since Linux 2.6.11. The buffer should allow space for up to 16 // bytes; the returned string will be null-terminated. return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; -#elif defined(__APPLE__) - // since macos(10.6), ios(3.2) +#elif defined(HAVE_PTHREAD_GETNAME_NP) return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0; #else #error "unsupported platform" @@ -57,14 +92,18 @@ class ThreadName static bool set(const char* name) { + SRT_ASSERT(name != NULL); #if defined(__linux__) // The name can be up to 16 bytes long, including the terminating // null byte. (If the length of the string, including the terminating // null byte, exceeds 16 bytes, the string is silently truncated.) return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; -#elif defined(__APPLE__) - // since macos(10.6), ios(3.2) +#elif defined(HAVE_PTHREAD_SETNAME_NP) + #if defined(__APPLE__) return pthread_setname_np(name) == 0; + #else + return pthread_setname_np(pthread_self(), name) == 0; + #endif #else #error "unsupported platform" #endif From 5e75c3221f227e86bbd4400fdc91d98f55cd0455 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 31 Aug 2021 16:27:03 +0200 Subject: [PATCH 151/683] [core] Placed ThreadName class inside srt namespace --- apps/srt-tunnel.cpp | 18 +++++++++--------- srtcore/threadname.h | 4 ++++ test/test_threadname.cpp | 2 ++ testing/srt-test-multiplex.cpp | 10 +++++----- testing/srt-test-relay.cpp | 4 ++-- testing/testactivemedia.cpp | 4 ++-- testing/testactivemedia.hpp | 2 +- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 2c4aacfb3..fd25fb998 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -223,17 +223,17 @@ class Engine Engine(Tunnel* p, Medium* m1, Medium* m2, const std::string& nid) : #ifdef HAVE_FULL_CXX11 - media {m1, m2}, + media {m1, m2}, #endif - parent_tunnel(p), nameid(nid) + parent_tunnel(p), nameid(nid) { #ifndef HAVE_FULL_CXX11 - // MSVC is not exactly C++11 compliant and complains around - // initialization of an array. - // Leaving this method of initialization for clarity and - // possibly more preferred performance. - media[0] = m1; - media[1] = m2; + // MSVC is not exactly C++11 compliant and complains around + // initialization of an array. + // Leaving this method of initialization for clarity and + // possibly more preferred performance. + media[0] = m1; + media[1] = m2; #endif } @@ -241,7 +241,7 @@ class Engine { Verb() << "START: " << media[DIR_IN]->uri() << " --> " << media[DIR_OUT]->uri(); std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); - ThreadName tn(thrn.c_str()); + srt::ThreadName tn(thrn.c_str()); thr = thread([this]() { Worker(); }); } diff --git a/srtcore/threadname.h b/srtcore/threadname.h index e392f6d8e..a3dbb475e 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -65,6 +65,8 @@ written by #include "common.h" #include "sync.h" +namespace srt { + class ThreadName { @@ -217,4 +219,6 @@ class ThreadName } }; +} // namespace srt + #endif diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp index f9b1d1d8c..67301f97b 100644 --- a/test/test_threadname.cpp +++ b/test/test_threadname.cpp @@ -4,6 +4,8 @@ #include "gtest/gtest.h" #include "threadname.h" +using namespace srt; + TEST(ThreadName, GetSet) { std::string name("getset"); diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 859547069..e2d5c8b89 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -154,14 +154,14 @@ struct MediumPair applog.Note() << sout.str(); } } - catch (Source::ReadEOF& x) + catch (const Source::ReadEOF&) { applog.Note() << "EOS - closing media for loop: " << name; src->Close(); tar->Close(); applog.Note() << "CLOSED: " << name; } - catch (std::runtime_error& x) + catch (const std::runtime_error& x) { applog.Note() << "INTERRUPTED: " << x.what(); src->Close(); @@ -196,7 +196,7 @@ class MediaBase med.name = name; // Ok, got this, so we can start transmission. - ThreadName tn(thread_name.c_str()); + srt::ThreadName tn(thread_name.c_str()); med.runner = thread( [&med]() { med.TransmissionLoop(); }); return med; @@ -577,7 +577,7 @@ int main( int argc, char** argv ) SrtModel m(up.host(), iport, up.parameters()); - ThreadName::set("main"); + srt::ThreadName::set("main"); // Note: for input, there must be an exactly defined // number of sources. The loop rolls up to all these sources. @@ -615,7 +615,7 @@ int main( int argc, char** argv ) break; } - ThreadName::set("main"); + srt::ThreadName::set("main"); } applog.Note() << "All local stream definitions covered. Waiting for interrupt/broken all connections."; diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 1214125b7..323718b51 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -392,7 +392,7 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin void SrtMainLoop::InputRunner() { - ThreadName::set("InputRN"); + srt::ThreadName::set("InputRN"); // An extra thread with a loop that reads from the external input // and writes into the SRT medium. When echoback mode is used, // this thread isn't started at all and instead the SRT reading @@ -438,7 +438,7 @@ void SrtMainLoop::run() std::ostringstream tns; tns << "Input:" << this; - ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str().c_str()); m_input_thr = thread([this] { try { InputRunner(); diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 765d22ef6..53f42a9a1 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -3,7 +3,7 @@ void SourceMedium::Runner() { - ThreadName::set("SourceRN"); + srt::ThreadName::set("SourceRN"); Verb() << VerbLock << "Starting SourceMedium: " << this; for (;;) @@ -63,7 +63,7 @@ MediaPacket SourceMedium::Extract() void TargetMedium::Runner() { - ThreadName::set("TargetRN"); + srt::ThreadName::set("TargetRN"); auto on_return_set = OnReturnSet(running, false); Verb() << VerbLock << "Starting TargetMedium: " << this; for (;;) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index 06a1cd922..fbb2f9dc8 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -58,7 +58,7 @@ struct Medium running = true; std::ostringstream tns; tns << typeid(*this).name() << ":" << this; - ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str().c_str()); thr = thread( [this] { RunnerBase(); } ); } From df95ecb66be7ecffdfbe2f0b54b77ef7b7184d36 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 1 Sep 2021 13:50:36 -0500 Subject: [PATCH 152/683] [core] Fix atomic for MacOS, FreeBSD, GCC<4.7, Clang<6 (#2104) --- CMakeLists.txt | 7 ++ srtcore/atomic.h | 151 ++++++++++++++++++++++++++++++++++------- srtcore/atomic_clock.h | 91 +++++++++++++++++++++++++ srtcore/sync.h | 74 ++------------------ 4 files changed, 233 insertions(+), 90 deletions(-) create mode 100644 srtcore/atomic_clock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3693b5b37..c5cfa1394 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,13 @@ option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) +# NOTE: Use ATOMIC_USE_SRT_SYNC_MUTEX and will override the auto-detection of the +# Atomic implemetation in srtcore/atomic.h. +option(ATOMIC_USE_SRT_SYNC_MUTEX "Use srt::sync::Mutex to Implement Atomics" OFF) +if (ATOMIC_USE_SRT_SYNC_MUTEX) + add_definitions(-DATOMIC_USE_SRT_SYNC_MUTEX=1) +endif() + set(TARGET_srt "srt" CACHE STRING "The name for the SRT library") # Use application-defined group reader diff --git a/srtcore/atomic.h b/srtcore/atomic.h index 5cedd396d..caec84fe1 100644 --- a/srtcore/atomic.h +++ b/srtcore/atomic.h @@ -61,16 +61,54 @@ line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED #endif -#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) -#define ATOMIC_USE_GCC_INTRINSICS +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + // NOTE: Defined at the top level. +#elif defined(__APPLE__) && (__cplusplus >= 201103L) + // NOTE: Does support c++11 std::atomic, but the compiler may or + // may not support GCC atomic intrinsics. So go ahead and use the + // std::atomic implementation. + #define ATOMIC_USE_CPP11_ATOMIC +#elif (defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)) \ + || defined(__xlc__) + // NOTE: Clang <6 does not support GCC __atomic_* intrinsics. I am unsure + // about Clang6. Since Clang sets __GNUC__ and __GNUC_MINOR__ of this era + // to <4.5, older Clang will catch the setting below to use the + // POSIX Mutex Implementation. + #define ATOMIC_USE_GCC_INTRINSICS +#elif defined(__GNUC__) \ + && ( (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) ) + // NOTE: The __atomic_* family of intrisics were introduced in GCC-4.7.0. + // NOTE: This follows #if defined(__clang__), because most if, not all, + // versions of Clang define __GNUC__ and __GNUC_MINOR__ but often define + // them to 4.4 or an even earlier version. Most of the newish versions + // of Clang also support GCC Atomic Intrisics even if they set GCC version + // macros to <4.7. + #define ATOMIC_USE_GCC_INTRINSICS +#elif defined(__GNUC__) && !defined(ATOMIC_USE_SRT_SYNC_MUTEX) + // NOTE: GCC compiler built-ins for atomic operations are pure + // compiler extensions prior to GCC-4.7 and were grouped into the + // the __sync_* family of functions. GCC-4.7, both the c++11 and C11 + // standards had been finalized, and GCC updated their built-ins to + // better reflect the new memory model and the new functions grouped + // into the __atomic_* family. Also the memory models were defined + // differently, than in pre 4.7. + // TODO: PORT to the pre GCC-4.7 __sync_* intrinsics. In the meantime use + // the POSIX Mutex Implementation. + #define ATOMIC_USE_SRT_SYNC_MUTEX 1 #elif defined(_MSC_VER) -#define ATOMIC_USE_MSVC_INTRINSICS -#include "atomic_msvc.h" + #define ATOMIC_USE_MSVC_INTRINSICS + #include "atomic_msvc.h" #elif __cplusplus >= 201103L -#define ATOMIC_USE_CPP11_ATOMIC -#include + #define ATOMIC_USE_CPP11_ATOMIC #else -#error Unsupported compiler / system. + #error Unsupported compiler / system. +#endif +// Include any necessary headers for the selected Atomic Implementation. +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + #include "sync.h" +#endif +#if defined(ATOMIC_USE_CPP11_ATOMIC) + #include #endif namespace srt { @@ -82,31 +120,62 @@ class atomic { sizeof(T) == 8, "Only types of size 1, 2, 4 or 8 are supported"); - atomic() : value_(static_cast(0)) {} + atomic() + : value_(static_cast(0)) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + , mutex_() +#endif + { + // No-Op + } + + explicit atomic(const T value) + : value_(value) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + , mutex_() +#endif + { + // No-Op + } - explicit atomic(const T value) : value_(value) {} + ~atomic() + { + // No-Op + } /// @brief Performs an atomic increment operation (value + 1). /// @returns The new value of the atomic object. T operator++() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = ++value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::increment(&value_); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return ++value_; +#else + #error "Implement Me." #endif } /// @brief Performs an atomic decrement operation (value - 1). /// @returns The new value of the atomic object. T operator--() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = --value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::decrement(&value_); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return --value_; +#else + #error "Implement Me." #endif } @@ -119,7 +188,16 @@ class atomic { /// @param new_val The new value to write to the atomic object. /// @returns True if new_value was written to the atomic object. bool compare_exchange(const T expected_val, const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + bool result = false; + if (expected_val == value_) + { + value_ = new_val; + result = true; + } + return result; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) T e = expected_val; return __atomic_compare_exchange_n( &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); @@ -127,9 +205,11 @@ class atomic { const T old_val = msvc::interlocked::compare_exchange(&value_, new_val, expected_val); return (old_val == expected_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) T e = expected_val; return value_.compare_exchange_weak(e, new_val); +#else + #error "Implement Me." #endif } @@ -140,12 +220,17 @@ class atomic { /// /// @param new_val The new value to write to the atomic object. void store(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + value_ = new_val; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) (void)msvc::interlocked::exchange(&value_, new_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) value_.store(new_val); +#else + #error "Implement Me." #endif } @@ -153,13 +238,19 @@ class atomic { /// @note Be careful about how this is used, since any operations on the /// returned value are inherently non-atomic. T load() const { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = value_; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) // TODO(m): Is there a better solution for MSVC? return value_; -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return value_; +#else + #error "Implement Me." #endif } @@ -171,12 +262,19 @@ class atomic { /// @param new_val The new value to write to the atomic object. /// @returns the old value. T exchange(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + ScopedLock lg_(mutex_); + const T t = value_; + value_ = new_val; + return t; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); #elif defined(ATOMIC_USE_MSVC_INTRINSICS) return msvc::interlocked::exchange(&value_, new_val); -#else +#elif defined(ATOMIC_USE_CPP11_ATOMIC) return value_.exchange(new_val); +#else + #error "Implement Me." #endif } @@ -190,10 +288,17 @@ class atomic { } private: -#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) +#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1) + T value_; + mutable Mutex mutex_; +#elif defined(ATOMIC_USE_GCC_INTRINSICS) volatile T value_; -#else +#elif defined(ATOMIC_USE_MSVC_INTRINSICS) + volatile T value_; +#elif defined(ATOMIC_USE_CPP11_ATOMIC) std::atomic value_; +#else + #error "Implement Me. (value_ type)" #endif ATOMIC_DISALLOW_COPY(atomic) diff --git a/srtcore/atomic_clock.h b/srtcore/atomic_clock.h new file mode 100644 index 000000000..e01012313 --- /dev/null +++ b/srtcore/atomic_clock.h @@ -0,0 +1,91 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_SYNC_ATOMIC_CLOCK_H +#define INC_SRT_SYNC_ATOMIC_CLOCK_H + +#include "sync.h" +#include "atomic.h" + +namespace srt +{ +namespace sync +{ + +template +class AtomicDuration +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicDuration() ATR_NOEXCEPT : dur(0) {} + + duration_type load() + { + int64_t val = dur.load(); + return duration_type(val); + } + + void store(const duration_type& d) + { + dur.store(d.count()); + } + + AtomicDuration& operator=(const duration_type& s) + { + dur = s.count(); + return *this; + } + + operator duration_type() const + { + return duration_type(dur); + } +}; + +template +class AtomicClock +{ + atomic dur; + typedef typename Clock::duration duration_type; + typedef typename Clock::time_point time_point_type; +public: + + AtomicClock() ATR_NOEXCEPT : dur(0) {} + + time_point_type load() const + { + int64_t val = dur.load(); + return time_point_type(duration_type(val)); + } + + void store(const time_point_type& d) + { + dur.store(uint64_t(d.time_since_epoch().count())); + } + + AtomicClock& operator=(const time_point_type& s) + { + dur = s.time_since_epoch().count(); + return *this; + } + + operator time_point_type() const + { + return time_point_type(duration_type(dur.load())); + } +}; + +} // namespace sync +} // namespace srt + +#endif // INC_SRT_SYNC_ATOMIC_CLOCK_H diff --git a/srtcore/sync.h b/srtcore/sync.h index d78aed980..00633da06 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -23,7 +23,6 @@ #define SRT_SYNC_CLOCK_STR "STDCXX_STEADY" #else #include -#include "atomic.h" // Defile clock type to use #ifdef IA32 @@ -233,72 +232,11 @@ inline Duration operator*(const int& lhs, const Duration -class AtomicDuration -{ - atomic dur; - typedef typename Clock::duration duration_type; - typedef typename Clock::time_point time_point_type; -public: - - AtomicDuration() ATR_NOEXCEPT : dur(0) {} - - duration_type load() - { - int64_t val = dur.load(); - return duration_type(val); - } - - void store(const duration_type& d) - { - dur.store(d.count()); - } - - AtomicDuration& operator=(const duration_type& s) - { - dur = s.count(); - return *this; - } - - operator duration_type() const - { - return duration_type(dur); - } -}; - -template -class AtomicClock -{ - atomic dur; - typedef typename Clock::duration duration_type; - typedef typename Clock::time_point time_point_type; -public: - - AtomicClock() ATR_NOEXCEPT : dur(0) {} - - time_point_type load() const - { - int64_t val = dur.load(); - return time_point_type(duration_type(val)); - } - - void store(const time_point_type& d) - { - dur.store(uint64_t(d.time_since_epoch().count())); - } - - AtomicClock& operator=(const time_point_type& s) - { - dur = s.time_since_epoch().count(); - return *this; - } - - operator time_point_type() const - { - return time_point_type(duration_type(dur.load())); - } -}; - +// NOTE: Moved the following class definitons to "atomic_clock.h" +// template +// class AtomicDuration; +// template +// class AtomicClock; /////////////////////////////////////////////////////////////////////////////// // @@ -939,4 +877,6 @@ int genRandomInt(int minVal, int maxVal); } // namespace sync } // namespace srt +#include "atomic_clock.h" + #endif // INC_SRT_SYNC_H From 2243388a0ddd3296c4264b3187901849c00adeb8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 2 Sep 2021 13:07:35 +0200 Subject: [PATCH 153/683] [core] Removed ThreadName(const char*). (#2113) Now only string& can be passed to avoid checking for NULL. --- apps/srt-tunnel.cpp | 4 ++-- srtcore/api.cpp | 8 +++----- srtcore/core.cpp | 6 +++--- srtcore/sync.cpp | 4 ++-- srtcore/sync.h | 4 ++-- srtcore/threadname.h | 26 +++++++++++++------------- test/test_threadname.cpp | 4 ++-- testing/srt-test-multiplex.cpp | 2 +- testing/srt-test-relay.cpp | 2 +- testing/testactivemedia.hpp | 2 +- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index fd25fb998..fa08759a1 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -240,8 +240,8 @@ class Engine void Start() { Verb() << "START: " << media[DIR_IN]->uri() << " --> " << media[DIR_OUT]->uri(); - std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); - srt::ThreadName tn(thrn.c_str()); + const std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id(); + srt::ThreadName tn(thrn); thr = thread([this]() { Worker(); }); } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7f6d7ecaa..71386a566 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -154,9 +154,7 @@ bool srt::CUDTSocket::readReady() if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) return true; if (m_UDT.m_bListening) - { - return m_QueuedSockets.size() > 0; - } + return !m_QueuedSockets.empty(); return broken(); } @@ -955,7 +953,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) s->m_Status = SRTS_OPENED; // copy address information of local node - s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); + s->core().m_pSndQueue->m_pChannel->getSockAddr(s->m_SelfAddr); return 0; } @@ -1535,7 +1533,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i HLOGC(aclog.Debug, log << "groupConnect: connecting a new socket with ISN=" << isn); connectIn(ns, target_addr, isn); } - catch (CUDTException& e) + catch (const CUDTException& e) { LOGC(aclog.Error, log << "groupConnect: socket @" << sid << " in group " << pg->id() << " failed to connect"); // We know it does belong to a group. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c887218d9..a5fe649d8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9395,10 +9395,10 @@ int srt::CUDT::processData(CUnit* in_unit) const string& tn = tns2.str(); - ThreadName tnkeep(tn.c_str()); - const char* thname = tn.c_str(); + ThreadName tnkeep(tn); + const string& thname = tn; #else - const char* thname = "SRT:TsbPd"; + const string thname = "SRT:TsbPd"; #endif if (!StartThread(m_RcvTsbPdThread, CUDT::tsbpd, this, thname)) return -1; diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 820e0a7f9..aa15c2471 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -79,9 +79,9 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) #ifdef ENABLE_STDCXX_SYNC -bool StartThread(CThread& th, ThreadFunc&& f, void* args, const char* name) +bool StartThread(CThread& th, ThreadFunc&& f, void* args, const string& name) #else -bool StartThread(CThread& th, void* (*f) (void*), void* args, const char* name) +bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name) #endif { ThreadName tn(name); diff --git a/srtcore/sync.h b/srtcore/sync.h index 00633da06..cd0288ad4 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -843,9 +843,9 @@ namespace this_thread /// #ifdef ENABLE_STDCXX_SYNC typedef void* (&ThreadFunc) (void*); -bool StartThread(CThread& th, ThreadFunc&& f, void* args, const char* name); +bool StartThread(CThread& th, ThreadFunc&& f, void* args, const std::string& name); #else -bool StartThread(CThread& th, void* (*f) (void*), void* args, const char* name); +bool StartThread(CThread& th, void* (*f) (void*), void* args, const std::string& name); #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/srtcore/threadname.h b/srtcore/threadname.h index a3dbb475e..3d60fbe4f 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -111,9 +111,9 @@ class ThreadName #endif } - ThreadNameImpl(const char* name) + explicit ThreadNameImpl(const char* name) + : reset(false) { - reset = false; tid = pthread_self(); if (!get(old_name)) @@ -140,6 +140,10 @@ class ThreadName if (tid == pthread_self()) set(old_name); } + + private: + ThreadNameImpl(ThreadNameImpl& other); + ThreadNameImpl& operator=(const ThreadNameImpl& other); private: bool reset; @@ -202,21 +206,17 @@ class ThreadName return ret; } - // note: set can fail if name is too long. The upper limit is platform - // dependent. strlen(name) <= 15 should work on most of the platform. - static bool set(const char* name) { return ThreadNameImpl::set(name); } - - static bool set(const std::string& name) { return set(name.c_str()); } - - ThreadName(const char* name) - : impl(name) - { - } + static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); } - ThreadName(const std::string& name) + explicit ThreadName(const std::string& name) : impl(name.c_str()) { } + +private: + ThreadName(const ThreadName&); + ThreadName(const char*); + ThreadName& operator=(const ThreadName& other); }; } // namespace srt diff --git a/test/test_threadname.cpp b/test/test_threadname.cpp index 67301f97b..0b03686a4 100644 --- a/test/test_threadname.cpp +++ b/test/test_threadname.cpp @@ -28,12 +28,12 @@ TEST(ThreadName, GetSet) TEST(ThreadName, AutoReset) { - std::string old_name("old"); + const std::string old_name("old"); std::string new_name("new-name"); if (ThreadName::DUMMY_IMPL) { // just make sure the API is correct - ThreadName t("test"); + ThreadName t(std::string("test")); return; } diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index e2d5c8b89..df810aea6 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -196,7 +196,7 @@ class MediaBase med.name = name; // Ok, got this, so we can start transmission. - srt::ThreadName tn(thread_name.c_str()); + srt::ThreadName tn(thread_name); med.runner = thread( [&med]() { med.TransmissionLoop(); }); return med; diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 323718b51..0f72b8a1c 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -438,7 +438,7 @@ void SrtMainLoop::run() std::ostringstream tns; tns << "Input:" << this; - srt::ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str()); m_input_thr = thread([this] { try { InputRunner(); diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index fbb2f9dc8..c7b11e583 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -58,7 +58,7 @@ struct Medium running = true; std::ostringstream tns; tns << typeid(*this).name() << ":" << this; - srt::ThreadName tn(tns.str().c_str()); + srt::ThreadName tn(tns.str()); thr = thread( [this] { RunnerBase(); } ); } From e8f4057590c0f5e7e05c7b8f76b763147f883bb7 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Fri, 3 Sep 2021 07:49:10 -0500 Subject: [PATCH 154/683] [build] Automatically link libatomic if needed. (#2116) --- CMakeLists.txt | 25 +++++- scripts/CheckCXXAtomic.cmake | 62 ++++++++++++++ scripts/CheckGCCAtomicIntrinsics.cmake | 110 +++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 scripts/CheckCXXAtomic.cmake create mode 100644 scripts/CheckGCCAtomicIntrinsics.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c5cfa1394..4e01e7e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -411,6 +411,20 @@ else() # Compiler altered by WITH_COMPILER_TYPE/PREFIX - can't rely on CMAKE_CXX message(STATUS "COMPILER CHANGED TO: ${COMPILER_TYPE} - forcing C++11 standard for apps") endif() +# Check for GCC Atomic Intrinsics and C++11 Atomics. +# Sets: +# HAVE_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC +include(CheckGCCAtomicIntrinsics) +CheckGCCAtomicIntrinsics() +# HAVE_CXX_ATOMIC +# HAVE_CXX_ATOMIC_STATIC +include(CheckCXXAtomic) +CheckCXXAtomic() + if (DISABLE_CXX11) set (ENABLE_CXX11 0) elseif( DEFINED ENABLE_CXX11 ) @@ -877,8 +891,6 @@ if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) - elseif (ANDROID) - target_link_libraries(${TARGET_srt}_shared PRIVATE atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_shared PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -913,8 +925,6 @@ if (srt_libspec_static) endif() elseif (MINGW) target_link_libraries(${TARGET_srt}_static PRIVATE wsock32 ws2_32) - elseif (ANDROID) - target_link_libraries(${TARGET_srt}_static PUBLIC atomic) endif() if (USE_GNUSTL) target_link_libraries(${TARGET_srt}_static PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) @@ -966,6 +976,13 @@ if (srt_libspec_shared) endif() endif() +# Required by some toolchains when statically linking this library if the +# GCC Atomic Intrinsics are being used. +if (HAVE_GCCATOMIC_INTRINSICS + AND HAVE_LIBATOMIC) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) +endif() + # Cygwin installs the *.dll libraries in bin directory and uses PATH. set (INSTALL_SHARED_DIR ${CMAKE_INSTALL_LIBDIR}) diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake new file mode 100644 index 000000000..e071b78a3 --- /dev/null +++ b/scripts/CheckCXXAtomic.cmake @@ -0,0 +1,62 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for c++11 std::atomic. +# +# Sets: +# HAVE_CXX_ATOMIC +# HAVE_CXX_ATOMIC_STATIC + +include(CheckCXXSourceCompiles) +include(CheckLibraryExists) + +function(CheckCXXAtomic) + + unset(HAVE_CXX_ATOMIC) + unset(HAVE_CXX_ATOMIC PARENT_SCOPE) + unset(HAVE_CXX_ATOMIC CACHE) + + unset(HAVE_CXX_ATOMIC_STATIC) + unset(HAVE_CXX_ATOMIC_STATIC PARENT_SCOPE) + unset(HAVE_CXX_ATOMIC_STATIC CACHE) + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckCXXAtomic_CODE + " + #include + #include + int main(void) + { + std::atomic x(0); + std::atomic y(0); + return x + y; + } + ") + + set(CMAKE_REQUIRED_FLAGS "-std=c++11") + + check_cxx_source_compiles( + "${CheckCXXAtomic_CODE}" + HAVE_CXX_ATOMIC) + + if(HAVE_CXX_ATOMIC) + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + check_cxx_source_compiles( + "${CheckCXXAtomic_CODE}" + HAVE_CXX_ATOMIC_STATIC) + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + +endfunction(CheckCXXAtomic) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake new file mode 100644 index 000000000..164fb190e --- /dev/null +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -0,0 +1,110 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Check for GCC Atomic Intrinsics and whether libatomic is required. +# +# Sets: +# HAVE_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC +# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC +# +# See +# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html +# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync + +include(CheckCSourceCompiles) +include(CheckLibraryExists) + +function(CheckGCCAtomicIntrinsics) + + unset(HAVE_LIBATOMIC) + unset(HAVE_LIBATOMIC PARENT_SCOPE) + unset(HAVE_LIBATOMIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS) + unset(HAVE_GCCATOMIC_INTRINSICS PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC CACHE) + + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC PARENT_SCOPE) + unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC CACHE) + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckGCCAtomicIntrinsics_CODE + " + #include + #include + int main(void) + { + ptrdiff_t x = 0; + intmax_t y = 0; + __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); + return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) + + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); + } + ") + + check_library_exists( + atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS) + + if (NOT HAVE_GCCATOMIC_INTRINSICS + AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + set(HAVE_GCCATOMIC_INTRINSICS TRUE PARENT_SCOPE) + endif() + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + if (HAVE_GCCATOMIC_INTRINSICS) + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_STATIC) + if (NOT HAVE_GCCATOMIC_INTRINSICS_STATIC + AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles( + "${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_STATIC) + if (HAVE_GCCATOMIC_INTRINSICS_STATIC) + set(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC TRUE PARENT_SCOPE) + endif() + endif() + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + +endfunction(CheckGCCAtomicIntrinsics) From 2e09a1696aba44e984e769eb2aa7c59a5905199a Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 6 Sep 2021 11:49:34 +0200 Subject: [PATCH 155/683] [build] Update Windows installer for alternate platform names (#2117) --- scripts/win-installer/libsrt.props | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props index d8de447c1..01e0b5dac 100644 --- a/scripts/win-installer/libsrt.props +++ b/scripts/win-installer/libsrt.props @@ -4,6 +4,25 @@ + + + + + Win32 + + + + + x64 + + + + + $(Platform) + + + + @@ -11,7 +30,7 @@ srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) - $(LIBSRT)\lib\$(Configuration)-$(Platform);%(AdditionalLibraryDirectories) + $(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories) /ignore:4099 %(AdditionalOptions) From e98146a2f2f75370fa7715ccf5f8a581cdb6ace8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 6 Sep 2021 14:37:40 +0200 Subject: [PATCH 156/683] [core] Avoid using strlen in ThreadName class --- srtcore/threadname.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/srtcore/threadname.h b/srtcore/threadname.h index 3d60fbe4f..4dade104a 100644 --- a/srtcore/threadname.h +++ b/srtcore/threadname.h @@ -111,7 +111,7 @@ class ThreadName #endif } - explicit ThreadNameImpl(const char* name) + explicit ThreadNameImpl(const std::string& name) : reset(false) { tid = pthread_self(); @@ -119,16 +119,16 @@ class ThreadName if (!get(old_name)) return; - reset = set(name); + reset = set(name.c_str()); if (reset) return; // Try with a shorter name. 15 is the upper limit supported by Linux, // other platforms should support a larger value. So 15 should works // on all platforms. - size_t max_len = 15; - if (std::strlen(name) > max_len) - reset = set(std::string(name, max_len).c_str()); + const size_t max_len = 15; + if (name.size() > max_len) + reset = set(name.substr(0, max_len).c_str()); } ~ThreadNameImpl() @@ -171,7 +171,7 @@ class ThreadName static bool set(const char*) { return false; } - ThreadNameImpl(const char*) {} + ThreadNameImpl(const std::string&) {} ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version { @@ -209,7 +209,7 @@ class ThreadName static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); } explicit ThreadName(const std::string& name) - : impl(name.c_str()) + : impl(name) { } From 34ba95157a0c27c85ee20450d4b481cec6c99f81 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Tue, 7 Sep 2021 12:43:28 +0200 Subject: [PATCH 157/683] Improved Access Control general syntax description --- docs/features/access-control.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/features/access-control.md b/docs/features/access-control.md index d583f795b..e6f9124c8 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -41,23 +41,23 @@ The Stream ID uses UTF-8 encoding. ## General Syntax -This recommended syntax starts with the characters known as an executable -specification in POSIX: `#!`. +The recommended syntax starts with the characters known as an executable specification in POSIX: `#!`. -The next two characters are: +The next character defines the format used for the following key-value pair syntax. +At the moment, there is only one supported syntax identified by `:` and described below. -- `:` - marks the format of the following key-value pair syntax (the only one defined currently). -- The content format, which is either: - - `:` - the comma-separated keys with no nesting - - `{` - like above, but nesting is allowed and must end with `}` +Everything that comes after a syntax identifier is further referenced as the content of the Stream ID. -(Nesting means that you can have multiple level brace-enclosed parts inside.) +The content starts with a `:` or `{` character identifying its format: -The form of the key-value pair is: +- `:` : comma-separated key-value pairs with no nesting, +- `{` : a nested block with one or several key-value pairs that must end with a `}` character. Nesting means that multiple level brace-enclosed parts are allowed. -```js -key1=value1,key2=value2... -``` +The form of the key-value pair is + +~~~ +key1=value1,key2=value2,... +~~~ ## Standard Keys From b147d5ec9ab7134b3eb8b5ef2b11039cf8bdd883 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 8 Sep 2021 04:28:27 -0500 Subject: [PATCH 158/683] [core] Fix Solaris build. (#2115) --- CMakeLists.txt | 11 +++++++--- apps/srt-tunnel.cpp | 22 ++++++++++---------- apps/transmitmedia.cpp | 5 ++++- srtcore/common.cpp | 2 +- srtcore/epoll.cpp | 3 ++- srtcore/list.cpp | 8 ++++---- srtcore/srt_compat.c | 2 +- srtcore/sync.h | 22 ++++++++++---------- srtcore/sync_cxx11.cpp | 4 ++-- srtcore/utilities.h | 40 +++++++++++++++++++++++++++++++++++++ testing/testactivemedia.cpp | 6 +++--- testing/testactivemedia.hpp | 6 +++--- 12 files changed, 90 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e01e7e63..3cb8d82a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,9 @@ set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) set_if(GNU ${CMAKE_SYSTEM_NAME} MATCHES "GNU") set_if(ANDROID ${SYSNAME_LC} MATCHES "android") -set_if(POSIX LINUX OR DARWIN OR BSD OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) -set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR CYGWIN OR GNU) +set_if(SUNOS "${SYSNAME_LC}" MATCHES "sunos") +set_if(POSIX LINUX OR DARWIN OR BSD OR SUNOS OR ANDROID OR (CYGWIN AND CYGWIN_USE_POSIX)) +set_if(SYMLINKABLE LINUX OR DARWIN OR BSD OR SUNOS OR CYGWIN OR GNU) # Not sure what to do in case of compiling by MSVC. # This will make installdir in C:\Program Files\SRT then @@ -282,8 +283,9 @@ if (DEFINED HAVE_INET_PTON) add_definitions(-DHAVE_INET_PTON=1) endif() +# Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* include(FindPThreadGetSetName) -FindPThreadGetSetName() # Defines HAVE_PTHREAD_GETNAME_* and HAVE_PTHREAD_SETNAME_* +FindPThreadGetSetName() if (ENABLE_MONOTONIC_CLOCK) if (NOT ENABLE_MONOTONIC_CLOCK_DEFAULT) @@ -617,6 +619,9 @@ elseif(CYGWIN) elseif(GNU) add_definitions(-DGNU=1) message(STATUS "DETECTED SYSTEM: GNU; GNU=1" ) +elseif(SUNOS) + add_definitions(-DSUNOS=1) + message(STATUS "DETECTED SYSTEM: SunOS|Solaris; SUNOS=1" ) else() message(FATAL_ERROR "Unsupported system: ${CMAKE_SYSTEM_NAME}") endif() diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index fa08759a1..ebc996c9a 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -94,7 +94,7 @@ class Medium bool m_eof = false; bool m_broken = false; - mutex access; // For closing + std::mutex access; // For closing template static Medium* CreateAcceptor(DerivedMedium* self, const sockaddr_any& sa, SocketType sock, size_t chunk) @@ -287,7 +287,7 @@ class Tunnel std::unique_ptr med_acp, med_clr; Engine acp_to_clr, clr_to_acp; volatile bool running = true; - mutex access; + std::mutex access; public: @@ -324,7 +324,7 @@ class Tunnel /* { - lock_guard lk(access); + lock_guard lk(access); if (acp_to_clr.stat() == -1 && clr_to_acp.stat() == -1) { Verb() << "Tunnel: Both engine decommissioned, will stop the tunnel."; @@ -438,7 +438,7 @@ class SrtMedium: public Medium void CloseSrt() { Verb() << "Closing SRT socket for " << uri(); - lock_guard lk(access); + lock_guard lk(access); if (m_socket == SRT_ERROR) return; srt_close(m_socket); @@ -528,7 +528,7 @@ class TcpMedium: public Medium void CloseTcp() { Verb() << "Closing TCP socket for " << uri(); - lock_guard lk(access); + lock_guard lk(access); if (m_socket == -1) return; tcp_close(m_socket); @@ -954,20 +954,20 @@ std::unique_ptr Medium::Create(const std::string& url, size_t chunk, Med struct Tunnelbox { list> tunnels; - mutex access; + std::mutex access; condition_variable decom_ready; bool main_running = true; thread thr; void signal_decommission() { - lock_guard lk(access); + lock_guard lk(access); decom_ready.notify_one(); } void install(std::unique_ptr&& acp, std::unique_ptr&& clr) { - lock_guard lk(access); + lock_guard lk(access); Verb() << "Tunnelbox: Starting tunnel: " << acp->uri() << " <-> " << clr->uri(); tunnels.emplace_back(new Tunnel(this, move(acp), move(clr))); @@ -992,7 +992,7 @@ struct Tunnelbox void CleanupWorker() { - unique_lock lk(access); + unique_lock lk(access); while (main_running) { @@ -1027,7 +1027,7 @@ void Tunnel::Stop() if (!running) return; // already stopped - lock_guard lk(access); + lock_guard lk(access); // Ok, you are the first to make the tunnel // not running and inform the tunnelbox. @@ -1037,7 +1037,7 @@ void Tunnel::Stop() bool Tunnel::decommission_if_dead(bool forced) { - lock_guard lk(access); + lock_guard lk(access); if (running && !forced) return false; // working, not to be decommissioned diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index e5f76206d..c61c703b8 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -22,9 +22,12 @@ #if !defined(_WIN32) #include #else -#include +#include #include #endif +#if defined(SUNOS) +#include +#endif #include "netinet_any.h" #include "apputil.hpp" diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 0e19dc611..cd151116a 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -98,7 +98,7 @@ const char* CUDTException::getErrorMessage() const ATR_NOTHROW return srt::strerror_get_message(m_iMajor, m_iMinor); } -string CUDTException::getErrorString() const +std::string CUDTException::getErrorString() const { return getErrorMessage(); } diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index d4919ff39..20a9c0bbb 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -149,7 +149,8 @@ ENOMEM: There was insufficient memory to create the kernel object. if (localid < 0) throw CUDTException(MJ_SETUP, MN_NONE, errno); #else - // on Solaris, use /dev/poll + // TODO: Solaris, use port_getn() + // https://docs.oracle.com/cd/E86824_01/html/E54766/port-get-3c.html // on Windows, select #endif diff --git a/srtcore/list.cpp b/srtcore/list.cpp index c7ab7cfc8..e295fb8c8 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -100,13 +100,13 @@ void CSndLossList::traceState() const int pos = m_iHead; while (pos != SRT_SEQNO_NONE) { - ::cout << pos << ":[" << m_caSeq[pos].seqstart; + std::cout << pos << ":[" << m_caSeq[pos].seqstart; if (m_caSeq[pos].seqend != SRT_SEQNO_NONE) - ::cout << ", " << m_caSeq[pos].seqend; - ::cout << "], "; + std::cout << ", " << m_caSeq[pos].seqend; + std::cout << "], "; pos = m_caSeq[pos].inext; } - ::cout << "\n"; + std::cout << "\n"; } int CSndLossList::insert(int32_t seqno1, int32_t seqno2) diff --git a/srtcore/srt_compat.c b/srtcore/srt_compat.c index 32c8dd6fb..1473a7b94 100644 --- a/srtcore/srt_compat.c +++ b/srtcore/srt_compat.c @@ -23,7 +23,7 @@ written by #include #include #include -#if defined(__unix__) && !defined(BSD) +#if defined(__unix__) && !defined(BSD) && !defined(SUNOS) #include #endif diff --git a/srtcore/sync.h b/srtcore/sync.h index cd0288ad4..7cb0f63a9 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -60,7 +60,6 @@ namespace srt { namespace sync { -using namespace std; /////////////////////////////////////////////////////////////////////////////// // @@ -71,7 +70,7 @@ using namespace std; #if ENABLE_STDCXX_SYNC template -using Duration = chrono::duration; +using Duration = std::chrono::duration; #else @@ -130,13 +129,13 @@ class Duration #if ENABLE_STDCXX_SYNC -using steady_clock = chrono::steady_clock; +using steady_clock = std::chrono::steady_clock; template -using time_point = chrono::time_point; +using time_point = std::chrono::time_point; template -using TimePoint = chrono::time_point; +using TimePoint = std::chrono::time_point; template inline bool is_zero(const time_point &tp) @@ -212,8 +211,8 @@ class TimePoint inline void operator-=(const Duration& rhs) { m_timestamp -= rhs.count(); } public: // - static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(numeric_limits::min()); } - static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(numeric_limits::max()); } + static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(std::numeric_limits::min()); } + static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(std::numeric_limits::max()); } public: Duration time_since_epoch() const; @@ -311,9 +310,9 @@ inline bool is_zero(const TimePoint& t) /////////////////////////////////////////////////////////////////////////////// #if ENABLE_STDCXX_SYNC -using Mutex = mutex; -using UniqueLock = unique_lock; -using ScopedLock = lock_guard; +using Mutex = std::mutex; +using UniqueLock = std::unique_lock; +using ScopedLock = std::lock_guard; #else /// Mutex is a class wrapper, that should mimic the std::chrono::mutex class. /// At the moment the extra function ref() is temporally added to allow calls @@ -471,7 +470,7 @@ class Condition private: #if ENABLE_STDCXX_SYNC - condition_variable m_cv; + std::condition_variable m_cv; #else pthread_cond_t m_cv; #endif @@ -742,6 +741,7 @@ class CGlobEvent #ifdef ENABLE_STDCXX_SYNC typedef std::system_error CThreadException; using CThread = std::thread; +namespace this_thread = std::this_thread; #else // pthreads wrapper version typedef ::CUDTException CThreadException; diff --git a/srtcore/sync_cxx11.cpp b/srtcore/sync_cxx11.cpp index 56a63a4bd..4dae3563e 100644 --- a/srtcore/sync_cxx11.cpp +++ b/srtcore/sync_cxx11.cpp @@ -68,12 +68,12 @@ void srt::sync::Condition::wait(UniqueLock& lock) bool srt::sync::Condition::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time) { // Another possible implementation is wait_until(steady_clock::now() + timeout); - return m_cv.wait_for(lock, rel_time) != cv_status::timeout; + return m_cv.wait_for(lock, rel_time) != std::cv_status::timeout; } bool srt::sync::Condition::wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time) { - return m_cv.wait_until(lock, timeout_time) != cv_status::timeout; + return m_cv.wait_until(lock, timeout_time) != std::cv_status::timeout; } void srt::sync::Condition::notify_one() diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 60a286ec0..f787a579e 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -140,6 +140,46 @@ written by # define le64toh(x) letoh64(x) #endif +#elif defined(SUNOS) + + // SunOS/Solaris + + #include + #include + + #define __LITTLE_ENDIAN 1234 + #define __BIG_ENDIAN 4321 + + # if defined(_BIG_ENDIAN) + #define __BYTE_ORDER __BIG_ENDIAN + #define be64toh(x) (x) + #define be32toh(x) (x) + #define be16toh(x) (x) + #define le16toh(x) ((uint16_t)BSWAP_16(x)) + #define le32toh(x) BSWAP_32(x) + #define le64toh(x) BSWAP_64(x) + #define htobe16(x) (x) + #define htole16(x) ((uint16_t)BSWAP_16(x)) + #define htobe32(x) (x) + #define htole32(x) BSWAP_32(x) + #define htobe64(x) (x) + #define htole64(x) BSWAP_64(x) + # else + #define __BYTE_ORDER __LITTLE_ENDIAN + #define be64toh(x) BSWAP_64(x) + #define be32toh(x) ntohl(x) + #define be16toh(x) ntohs(x) + #define le16toh(x) (x) + #define le32toh(x) (x) + #define le64toh(x) (x) + #define htobe16(x) htons(x) + #define htole16(x) (x) + #define htobe32(x) htonl(x) + #define htole32(x) (x) + #define htobe64(x) BSWAP_64(x) + #define htole64(x) (x) + # endif + #elif defined(__WINDOWS__) # include diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 53f42a9a1..7d062f49d 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -16,7 +16,7 @@ void SourceMedium::Runner() } LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", input.payload.size(), "] MEDIUM -> BUFFER. signal(", &ready, ")"); - lock_guard g(buffer_lock); + lock_guard g(buffer_lock); buffer.push_back(input); ready.notify_one(); } @@ -24,7 +24,7 @@ void SourceMedium::Runner() MediaPacket SourceMedium::Extract() { - unique_lock g(buffer_lock); + unique_lock g(buffer_lock); for (;;) { if (::transmit_int_state) @@ -70,7 +70,7 @@ void TargetMedium::Runner() { MediaPacket val; { - unique_lock lg(buffer_lock); + unique_lock lg(buffer_lock); if (buffer.empty()) { if (!running) diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index c7b11e583..f4bc360ba 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -150,7 +150,7 @@ struct TargetMedium: Medium bool Schedule(const MediaPacket& data) { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); - lock_guard lg(buffer_lock); + lock_guard lg(buffer_lock); LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); if (!running || ::transmit_int_state) { @@ -166,13 +166,13 @@ struct TargetMedium: Medium void Clear() { - lock_guard lg(buffer_lock); + lock_guard lg(buffer_lock); buffer.clear(); } void Interrupt() { - lock_guard lg(buffer_lock); + lock_guard lg(buffer_lock); running = false; ready.notify_one(); } From ad84c38e40ba36e18699dd5998684540620697a1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 14 Sep 2021 12:11:22 +0200 Subject: [PATCH 159/683] [core] Fixed FileCC random decrease --- srtcore/congctl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 2301dfc68..b394f8183 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -535,8 +535,7 @@ class FileCC : public SrtCongestionControlBase m_iLastDecSeq = m_parent->sndSeqNo(); - // remove global synchronization using randomization. - m_iDecRandom = genRandomInt(1, m_iAvgNAKNum); + m_iDecRandom = m_iAvgNAKNum > 1 ? genRandomInt(1, m_iAvgNAKNum) : 1; SRT_ASSERT(m_iDecRandom >= 1); HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin << ", lastsentseqno=" << m_iLastDecSeq From 4d0fe6166768d524d28e35e35c72bc12de0d46b5 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Thu, 16 Sep 2021 05:00:15 -0500 Subject: [PATCH 160/683] [build] Fixes build with CMake <3.0.2. (#2123) --- scripts/CheckCXXAtomic.cmake | 10 +--- scripts/CheckGCCAtomicIntrinsics.cmake | 25 ++------ scripts/FindPThreadGetSetName.cmake | 80 ++++++++++++-------------- scripts/UnSetVariableFull.cmake | 27 +++++++++ 4 files changed, 72 insertions(+), 70 deletions(-) create mode 100644 scripts/UnSetVariableFull.cmake diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake index e071b78a3..8c2d141a1 100644 --- a/scripts/CheckCXXAtomic.cmake +++ b/scripts/CheckCXXAtomic.cmake @@ -15,16 +15,12 @@ include(CheckCXXSourceCompiles) include(CheckLibraryExists) +include(UnSetVariableFull) function(CheckCXXAtomic) - unset(HAVE_CXX_ATOMIC) - unset(HAVE_CXX_ATOMIC PARENT_SCOPE) - unset(HAVE_CXX_ATOMIC CACHE) - - unset(HAVE_CXX_ATOMIC_STATIC) - unset(HAVE_CXX_ATOMIC_STATIC PARENT_SCOPE) - unset(HAVE_CXX_ATOMIC_STATIC CACHE) + UnSetVariableFull(HAVE_CXX_ATOMIC) + UnSetVariableFull(HAVE_CXX_ATOMIC_STATIC) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 164fb190e..8af1bd091 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -22,28 +22,15 @@ include(CheckCSourceCompiles) include(CheckLibraryExists) +include(UnSetVariableFull) function(CheckGCCAtomicIntrinsics) - unset(HAVE_LIBATOMIC) - unset(HAVE_LIBATOMIC PARENT_SCOPE) - unset(HAVE_LIBATOMIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS) - unset(HAVE_GCCATOMIC_INTRINSICS PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC CACHE) - - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC PARENT_SCOPE) - unset(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC CACHE) + UnSetVariableFull(HAVE_LIBATOMIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC) + UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake index fa818fd73..c78bca2be 100644 --- a/scripts/FindPThreadGetSetName.cmake +++ b/scripts/FindPThreadGetSetName.cmake @@ -27,56 +27,48 @@ # add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) include(CheckSymbolExists) +include(UnSetVariableFull) function(FindPThreadGetSetName) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) - unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H PARENT_SCOPE) - unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP) + UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP) - unset(HAVE_PTHREAD_GETNAME_NP) - unset(HAVE_PTHREAD_GETNAME_NP PARENT_SCOPE) - unset(HAVE_PTHREAD_GETNAME_NP CACHE) - unset(HAVE_PTHREAD_SETNAME_NP) - unset(HAVE_PTHREAD_SETNAME_NP PARENT_SCOPE) - unset(HAVE_PTHREAD_SETNAME_NP CACHE) + set(CMAKE_REQUIRED_DEFINITIONS + -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) + set(CMAKE_REQUIRED_FLAGS "-pthread") - set(CMAKE_REQUIRED_DEFINITIONS - -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) - set(CMAKE_REQUIRED_FLAGS "-pthread") + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") + check_symbol_exists( + pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) + endif() + check_symbol_exists( + pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) + endif() - message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread_np.h':") - check_symbol_exists( - pthread_getname_np "pthread_np.h" HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - add_definitions(-DHAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H=1) - endif() - check_symbol_exists( - pthread_setname_np "pthread_np.h" HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - add_definitions(-DHAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H=1) - endif() + message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") + check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) + if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + endif() + check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) + if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) + set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + endif() + if (HAVE_PTHREAD_GETNAME_NP) + add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) + endif() + if (HAVE_PTHREAD_SETNAME_NP) + add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) + endif() - message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") - check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) - if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) - endif() - check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) - if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) - endif() - if (HAVE_PTHREAD_GETNAME_NP) - add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) - endif() - if (HAVE_PTHREAD_SETNAME_NP) - add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) - endif() - - unset(CMAKE_REQUIRED_DEFINITIONS) - unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_FLAGS) endfunction(FindPThreadGetSetName) diff --git a/scripts/UnSetVariableFull.cmake b/scripts/UnSetVariableFull.cmake new file mode 100644 index 000000000..f751374ee --- /dev/null +++ b/scripts/UnSetVariableFull.cmake @@ -0,0 +1,27 @@ +# +# SRT - Secure, Reliable, Transport +# Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# Notes: +# +# Macro that UnSets a variable in Cache, Local Scope, and Parent Scope. +# +# Usage: +# +# UnSetVariableFull() + +macro(UnSetVariableFull tVariable) + unset(tVariable) + unset(tVariable CACHE) + # unset(.... PARENT_SCOPE) was introduced in cmake-3.0.2. + if ("${CMAKE_VERSION}" VERSION_LESS "3.0.2") + set(tVariable "" PARENT_SCOPE) + else() + unset(tVariable PARENT_SCOPE) + endif() +endmacro() From f9a54a033bf0ece3071f0fb297eb1130b8d9de7d Mon Sep 17 00:00:00 2001 From: Yanko Kaneti Date: Sat, 18 Sep 2021 21:53:44 +0300 Subject: [PATCH 161/683] [build] Build but do not install test-srt --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cb8d82a6..d1e7929e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1062,10 +1062,14 @@ else() message(FATAL_ERROR "Either ENABLE_STATIC or ENABLE_SHARED has to be ON!") endif() -macro(srt_add_program name) +macro(srt_add_program_dont_install name) add_executable(${name} ${ARGN}) target_include_directories(${name} PRIVATE apps) target_include_directories(${name} PRIVATE common) +endmacro() + +macro(srt_add_program name) + srt_add_program_dont_install(${name} ${ARGN}) install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endmacro() @@ -1276,7 +1280,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) SOURCES SOURCES_unittests ) - srt_add_program(test-srt ${SOURCES_unittests}) + srt_add_program_dont_install(test-srt ${SOURCES_unittests}) srt_make_application(test-srt) target_include_directories(test-srt PRIVATE ${SSL_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) From add40584757636ec99eef41ea3ea4687a43feb3f Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 20 Sep 2021 16:45:53 +0200 Subject: [PATCH 162/683] [core] Fixed 'undef' warning with gcc and clang (#2131) --- srtcore/platform_sys.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index 79760080e..cb5d0afd9 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -45,7 +45,15 @@ #endif #else -#if __APPLE__ +#if defined(__APPLE__) && __APPLE__ +// Warning: please keep this test as it is, do not make it +// "#if __APPLE__" or "#ifdef __APPLE__". In applications with +// a strict "no warning policy", "#if __APPLE__" triggers an "undef" +// error. With GCC, an old & never fixed bug prevents muting this +// warning (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431). +// Before this fix, the solution was to "#define __APPLE__ 0" before +// including srt.h. So, don't use "#ifdef __APPLE__" either. + // XXX Check if this condition doesn't require checking of // also other macros, like TARGET_OS_IOS etc. From 1d862c43c87678b2f02415ce958dd822786572a2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 22 Sep 2021 17:14:41 +0200 Subject: [PATCH 163/683] [build] Fixed a typo 'availabe' --- scripts/haiUtil.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/haiUtil.cmake b/scripts/haiUtil.cmake index 9e4fb4d56..222661989 100644 --- a/scripts/haiUtil.cmake +++ b/scripts/haiUtil.cmake @@ -252,7 +252,7 @@ function (test_requires_clock_gettime _enable _linklib) check_testcode_compiles(${code} "" HAVE_CLOCK_GETTIME_IN) if (HAVE_CLOCK_GETTIME_IN) - message(STATUS "CLOCK_MONOTONIC: availabe, no extra libs needed") + message(STATUS "CLOCK_MONOTONIC: available, no extra libs needed") set (${_enable} ON PARENT_SCOPE) set (${_linklib} "" PARENT_SCOPE) return() From 224042a5867b11a06ddb5fb0f40d93ffb2b22494 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Wed, 22 Sep 2021 11:38:14 +0200 Subject: [PATCH 164/683] [docs] Updated SRTO_RETRANSMITALGO socket option description --- docs/API/API-socket-options.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 84606135a..7f85d6800 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1277,17 +1277,20 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | | `SRTO_RETRANSMITALGO` | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | -Retransmission algorithm to use (SENDER option): +An SRT sender option to choose between two retransmission algorithms: -- 0 - Retransmit on every loss report (higher overhead, but slightly higher chance to recover a lost packet). -- 1 - Reduced retransmissions (retransmit not more often than once per RTT); reduced - bandwidth consumption. +- 0 - an intensive retransmission algorithm (default until SRT v1.4.4), and +- 1 - a new efficient retransmission algorithm (introduced in SRT v1.4.2; default since SRT v1.4.4). -This option is effective only on the sending side. It influences the decision +The intensive retransmission algorithm causes the SRT sender to schedule a packet for retransmission each time it receives a negative acknowledgement (NAK). On a network characterized by low packet loss levels and link capacity high enough to accommodate extra retransmission overhead, this algorithm increases the chances of recovering from packet loss with a minimum delay, and may better suit end-to-end latency constraints. + +The new efficient algorithm optimizes the bandwidth usage by producing fewer retransmissions per lost packet. It takes SRT statistics into account to determine if a retransmitted packet is still in flight and could reach the receiver in time, so that some of the NAK reports are ignored by the sender. This algorithm better fits general use cases, as well as cases where channel bandwidth is limited. + +NOTE: This option is effective only on the sending side. It influences the decision as to whether a particular reported lost packet should be retransmitted at a certain time or not. -The reduced retransmission algorithm (`SRTO_RETRANSMITALGO=1`) is only operational when receiver sends +NOTE: The efficient retransmission algorithm can only be used when a receiver sends Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). [Return to list](#list-of-options) From d8127a813d21909149fa94690845ec95d17977c8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 23 Sep 2021 11:54:00 +0200 Subject: [PATCH 165/683] [build] Fixed CMake warning about string --- scripts/iOS.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/iOS.cmake b/scripts/iOS.cmake index 73544a29b..dfb6aeb97 100644 --- a/scripts/iOS.cmake +++ b/scripts/iOS.cmake @@ -151,10 +151,10 @@ if (NOT DEFINED IOS_ARCH) set (IOS_ARCH x86_64) endif (${IOS_PLATFORM} STREQUAL OS) endif(NOT DEFINED IOS_ARCH) -set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") +set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS") # Set the find root to the iOS developer roots and to user defined paths -set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") +set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root") # default to searching for frameworks first set (CMAKE_FIND_FRAMEWORK FIRST) From 60891f31bb3f167497237e963b142fa777eecb50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 28 Sep 2021 14:53:02 +0200 Subject: [PATCH 166/683] [core] Fixed 'atomic' linking for iOS target (#2137) Co-authored-by: Jose Santiago --- CMakeLists.txt | 20 +++++++------ scripts/CheckCXXAtomic.cmake | 12 +++++--- scripts/CheckGCCAtomicIntrinsics.cmake | 40 ++++---------------------- scripts/FindPThreadGetSetName.cmake | 13 ++++----- scripts/UnSetVariableFull.cmake | 27 ----------------- 5 files changed, 30 insertions(+), 82 deletions(-) delete mode 100644 scripts/UnSetVariableFull.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d1e7929e1..7200491df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -418,8 +418,6 @@ endif() # HAVE_LIBATOMIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC include(CheckGCCAtomicIntrinsics) CheckGCCAtomicIntrinsics() # HAVE_CXX_ATOMIC @@ -887,7 +885,7 @@ if (srt_libspec_shared) endif() if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PRIVATE ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_shared PRIVATE crypt32.lib) else() set_target_properties(${TARGET_srt}_shared PROPERTIES LINK_FLAGS "/DELAYLOAD:libeay32.dll") @@ -925,7 +923,7 @@ if (srt_libspec_static) endif() if (MICROSOFT) target_link_libraries(${TARGET_srt}_static PRIVATE ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_static PRIVATE crypt32.lib) endif() elseif (MINGW) @@ -938,8 +936,8 @@ endif() target_include_directories(srt_virtual PRIVATE ${SSL_INCLUDE_DIRS}) -if (MICROSOFT) - if (OPENSSL_USE_STATIC_LIBS) +if (MICROSOFT) + if (OPENSSL_USE_STATIC_LIBS) set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ws2_32.lib crypt32.lib) else() set (SRT_LIBS_PRIVATE ${SRT_LIBS_PRIVATE} ws2_32.lib) @@ -983,9 +981,13 @@ endif() # Required by some toolchains when statically linking this library if the # GCC Atomic Intrinsics are being used. -if (HAVE_GCCATOMIC_INTRINSICS - AND HAVE_LIBATOMIC) - target_link_libraries(${TARGET_srt}_static PUBLIC atomic) +if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC AND HAVE_LIBATOMIC) + if (srt_libspec_static) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) + endif() + if (srt_libspec_shared) + target_link_libraries(${TARGET_srt}_shared PUBLIC atomic) + endif() endif() # Cygwin installs the *.dll libraries in bin directory and uses PATH. diff --git a/scripts/CheckCXXAtomic.cmake b/scripts/CheckCXXAtomic.cmake index 8c2d141a1..d3a3b9e71 100644 --- a/scripts/CheckCXXAtomic.cmake +++ b/scripts/CheckCXXAtomic.cmake @@ -15,12 +15,11 @@ include(CheckCXXSourceCompiles) include(CheckLibraryExists) -include(UnSetVariableFull) function(CheckCXXAtomic) - UnSetVariableFull(HAVE_CXX_ATOMIC) - UnSetVariableFull(HAVE_CXX_ATOMIC_STATIC) + unset(HAVE_CXX_ATOMIC CACHE) + unset(HAVE_CXX_ATOMIC_STATIC CACHE) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) @@ -45,7 +44,12 @@ function(CheckCXXAtomic) HAVE_CXX_ATOMIC) if(HAVE_CXX_ATOMIC) - set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + # CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14. + if(CMAKE_VERSION VERSION_LESS "3.14") + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + else() + set(CMAKE_REQUIRED_FLAGS "-std=c++11 -static") + endif() check_cxx_source_compiles( "${CheckCXXAtomic_CODE}" HAVE_CXX_ATOMIC_STATIC) diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 8af1bd091..9429db12f 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -13,8 +13,6 @@ # HAVE_LIBATOMIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC -# HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC # # See # https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html @@ -22,15 +20,12 @@ include(CheckCSourceCompiles) include(CheckLibraryExists) -include(UnSetVariableFull) function(CheckGCCAtomicIntrinsics) - UnSetVariableFull(HAVE_LIBATOMIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC) - UnSetVariableFull(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC) + unset(HAVE_LIBATOMIC CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) @@ -54,12 +49,12 @@ function(CheckGCCAtomicIntrinsics) check_library_exists( atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 check_c_source_compiles( "${CheckGCCAtomicIntrinsics_CODE}" HAVE_GCCATOMIC_INTRINSICS) - if (NOT HAVE_GCCATOMIC_INTRINSICS - AND HAVE_LIBATOMIC) + if (NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) set(CMAKE_REQUIRED_LIBRARIES "atomic") check_c_source_compiles( "${CheckGCCAtomicIntrinsics_CODE}" @@ -69,29 +64,4 @@ function(CheckGCCAtomicIntrinsics) endif() endif() - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) - - if (HAVE_GCCATOMIC_INTRINSICS) - set(CMAKE_REQUIRED_LINK_OPTIONS "-static") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_STATIC) - if (NOT HAVE_GCCATOMIC_INTRINSICS_STATIC - AND HAVE_LIBATOMIC) - set(CMAKE_REQUIRED_LIBRARIES "atomic") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_STATIC) - if (HAVE_GCCATOMIC_INTRINSICS_STATIC) - set(HAVE_GCCATOMIC_INTRINSICS_STATIC_REQUIRES_LIBATOMIC TRUE PARENT_SCOPE) - endif() - endif() - endif() - - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) - endfunction(CheckGCCAtomicIntrinsics) diff --git a/scripts/FindPThreadGetSetName.cmake b/scripts/FindPThreadGetSetName.cmake index c78bca2be..65685e1eb 100644 --- a/scripts/FindPThreadGetSetName.cmake +++ b/scripts/FindPThreadGetSetName.cmake @@ -27,14 +27,13 @@ # add_definitions(-DHAVE_PTHREAD_SETNAME_NP=1) include(CheckSymbolExists) -include(UnSetVariableFull) function(FindPThreadGetSetName) - UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - UnSetVariableFull(HAVE_PTHREAD_GETNAME_NP) - UnSetVariableFull(HAVE_PTHREAD_SETNAME_NP) + unset(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H CACHE) + unset(HAVE_PTHREAD_GETNAME_NP CACHE) + unset(HAVE_PTHREAD_SETNAME_NP CACHE) set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE -D_DARWIN_C_SOURCE -D_POSIX_SOURCE=1) @@ -55,11 +54,11 @@ function(FindPThreadGetSetName) message(STATUS "Checking for pthread_(g/s)etname_np in 'pthread.h':") check_symbol_exists(pthread_getname_np "pthread.h" HAVE_PTHREAD_GETNAME_NP) if (HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_GETNAME_NP TRUE PARENT_SCOPE) + set(HAVE_PTHREAD_GETNAME_NP 1 CACHE INTERNAL "" FORCE) endif() check_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) if (HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) - set(HAVE_PTHREAD_SETNAME_NP TRUE PARENT_SCOPE) + set(HAVE_PTHREAD_SETNAME_NP 1 CACHE INTERNAL "" FORCE) endif() if (HAVE_PTHREAD_GETNAME_NP) add_definitions(-DHAVE_PTHREAD_GETNAME_NP=1) diff --git a/scripts/UnSetVariableFull.cmake b/scripts/UnSetVariableFull.cmake deleted file mode 100644 index f751374ee..000000000 --- a/scripts/UnSetVariableFull.cmake +++ /dev/null @@ -1,27 +0,0 @@ -# -# SRT - Secure, Reliable, Transport -# Copyright (c) 2021 Haivision Systems Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# - -# Notes: -# -# Macro that UnSets a variable in Cache, Local Scope, and Parent Scope. -# -# Usage: -# -# UnSetVariableFull() - -macro(UnSetVariableFull tVariable) - unset(tVariable) - unset(tVariable CACHE) - # unset(.... PARENT_SCOPE) was introduced in cmake-3.0.2. - if ("${CMAKE_VERSION}" VERSION_LESS "3.0.2") - set(tVariable "" PARENT_SCOPE) - else() - unset(tVariable PARENT_SCOPE) - endif() -endmacro() From 6fda50220aa848145fb97d5382e66aa59cb81bff Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 28 Sep 2021 15:15:28 +0200 Subject: [PATCH 167/683] [build] Added iOS build to GitHub Actions CI (#2136) --- .github/workflows/iOS.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/iOS.yaml diff --git a/.github/workflows/iOS.yaml b/.github/workflows/iOS.yaml new file mode 100644 index 000000000..60d1e8d3a --- /dev/null +++ b/.github/workflows/iOS.yaml @@ -0,0 +1,25 @@ +name: iOS + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + strategy: + matrix: + cxxstdsync: [OFF, ON] + + name: iOS-cxxsync${{ matrix.cxxstdsync }} + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: configure + run: | + mkdir _build && cd _build + cmake ../ -DENABLE_ENCRYPTION=OFF -DENABLE_STDCXX_SYNC=${{matrix.cxxstdsync}} -DENABLE_UNITTESTS=OFF -DENABLE_EXPERIMENTAL_BONDING=ON --toolchain scripts/iOS.cmake + - name: build + run: cd _build && cmake --build ./ From cdec1145c8430a66b535636a14139531e51d9cb1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Tue, 28 Sep 2021 16:45:36 +0200 Subject: [PATCH 168/683] [build] Fix build Android with NDK <17. Need to link against libatomic when compiling statically --- CMakeLists.txt | 10 +- scripts/CheckGCCAtomicIntrinsics.cmake | 136 +++++++++++++++++-------- 2 files changed, 100 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7200491df..21478c36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,6 +416,8 @@ endif() # Check for GCC Atomic Intrinsics and C++11 Atomics. # Sets: # HAVE_LIBATOMIC +# HAVE_LIBATOMIC_COMPILES +# HAVE_LIBATOMIC_COMPILES_STATIC # HAVE_GCCATOMIC_INTRINSICS # HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC include(CheckGCCAtomicIntrinsics) @@ -973,7 +975,7 @@ endif() if (srt_libspec_shared) if (MICROSOFT) target_link_libraries(${TARGET_srt}_shared PUBLIC Ws2_32.lib) - if (OPENSSL_USE_STATIC_LIBS) + if (OPENSSL_USE_STATIC_LIBS) target_link_libraries(${TARGET_srt}_shared PUBLIC crypt32.lib) endif() endif() @@ -988,6 +990,12 @@ if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC AND HAVE_LIBATOMIC) if (srt_libspec_shared) target_link_libraries(${TARGET_srt}_shared PUBLIC atomic) endif() +elseif (HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES_STATIC) + # This is a workaround for ANDROID NDK<17 builds, which need to link + # to libatomic when linking statically to the SRT library. + if (srt_libspec_static) + target_link_libraries(${TARGET_srt}_static PUBLIC atomic) + endif() endif() # Cygwin installs the *.dll libraries in bin directory and uses PATH. diff --git a/scripts/CheckGCCAtomicIntrinsics.cmake b/scripts/CheckGCCAtomicIntrinsics.cmake index 9429db12f..f47b14d61 100644 --- a/scripts/CheckGCCAtomicIntrinsics.cmake +++ b/scripts/CheckGCCAtomicIntrinsics.cmake @@ -1,67 +1,113 @@ # -# SRT - Secure, Reliable, Transport -# Copyright (c) 2021 Haivision Systems Inc. +# SRT - Secure, Reliable, Transport Copyright (c) 2021 Haivision Systems Inc. # -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at http://mozilla.org/MPL/2.0/. # # Check for GCC Atomic Intrinsics and whether libatomic is required. # # Sets: -# HAVE_LIBATOMIC -# HAVE_GCCATOMIC_INTRINSICS -# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC +# HAVE_LIBATOMIC +# HAVE_LIBATOMIC_COMPILES +# HAVE_LIBATOMIC_COMPILES_STATIC +# HAVE_GCCATOMIC_INTRINSICS +# HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC # # See -# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html -# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync +# https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html +# https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync include(CheckCSourceCompiles) include(CheckLibraryExists) function(CheckGCCAtomicIntrinsics) - unset(HAVE_LIBATOMIC CACHE) - unset(HAVE_GCCATOMIC_INTRINSICS CACHE) - unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) + unset(HAVE_LIBATOMIC CACHE) + unset(HAVE_LIBATOMIC_COMPILES CACHE) + unset(HAVE_LIBATOMIC_COMPILES_STATIC CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS CACHE) + unset(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC CACHE) - unset(CMAKE_REQUIRED_FLAGS) - unset(CMAKE_REQUIRED_LIBRARIES) - unset(CMAKE_REQUIRED_LINK_OPTIONS) + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 - set(CheckGCCAtomicIntrinsics_CODE - " - #include - #include - int main(void) - { - ptrdiff_t x = 0; - intmax_t y = 0; - __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); - __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); - return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) - + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); - } - ") + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) - check_library_exists( - atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) + # Check for existance of libatomic and whether this symbol is present. + check_library_exists(atomic __atomic_fetch_add_8 "" HAVE_LIBATOMIC) - set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS) + set(CheckLibAtomicCompiles_CODE + " + int main(void) + { + const int result = 0; + return result; + } + ") - if (NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) - set(CMAKE_REQUIRED_LIBRARIES "atomic") - check_c_source_compiles( - "${CheckGCCAtomicIntrinsics_CODE}" - HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - if (HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) - set(HAVE_GCCATOMIC_INTRINSICS TRUE PARENT_SCOPE) - endif() - endif() + set(CMAKE_REQUIRED_LIBRARIES "atomic") + + # Check that the compiler can build a simple application and link with + # libatomic. + check_c_source_compiles("${CheckLibAtomicCompiles_CODE}" + HAVE_LIBATOMIC_COMPILES) + if(NOT HAVE_LIBATOMIC_COMPILES) + set(HAVE_LIBATOMIC + 0 + CACHE INTERNAL "" FORCE) + endif() + if(HAVE_LIBATOMIC AND HAVE_LIBATOMIC_COMPILES) + # CMAKE_REQUIRED_LINK_OPTIONS was introduced in CMake 3.14. + if(CMAKE_VERSION VERSION_LESS "3.14") + set(CMAKE_REQUIRED_LINK_OPTIONS "-static") + else() + set(CMAKE_REQUIRED_FLAGS "-static") + endif() + # Check that the compiler can build a simple application and statically link + # with libatomic. + check_c_source_compiles("${CheckLibAtomicCompiles_CODE}" + HAVE_LIBATOMIC_COMPILES_STATIC) + else() + set(HAVE_LIBATOMIC_COMPILES_STATIC + 0 + CACHE INTERNAL "" FORCE) + endif() + + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_OPTIONS) + + set(CheckGCCAtomicIntrinsics_CODE + " + #include + #include + int main(void) + { + ptrdiff_t x = 0; + intmax_t y = 0; + __atomic_add_fetch(&x, 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(&y, 1, __ATOMIC_SEQ_CST); + return __atomic_sub_fetch(&x, 1, __ATOMIC_SEQ_CST) + + __atomic_sub_fetch(&y, 1, __ATOMIC_SEQ_CST); + } + ") + + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # CMake 3.6 + check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS) + + if(NOT HAVE_GCCATOMIC_INTRINSICS AND HAVE_LIBATOMIC) + set(CMAKE_REQUIRED_LIBRARIES "atomic") + check_c_source_compiles("${CheckGCCAtomicIntrinsics_CODE}" + HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + if(HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC) + set(HAVE_GCCATOMIC_INTRINSICS + 1 + CACHE INTERNAL "" FORCE) + endif() + endif() endfunction(CheckGCCAtomicIntrinsics) From 167b8e5031ee4e0aed9a49365a7f458e53c1f68b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 29 Sep 2021 19:06:33 +0200 Subject: [PATCH 169/683] [core] Fixed group drift synchronization (#2139) --- srtcore/buffer.cpp | 13 +++++------ srtcore/buffer.h | 8 ++----- srtcore/core.cpp | 7 ++---- srtcore/group.cpp | 49 ++++++++++++------------------------------ srtcore/group.h | 5 ++++- srtcore/tsbpd_time.cpp | 8 +------ srtcore/tsbpd_time.h | 7 +----- 7 files changed, 29 insertions(+), 68 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index bbe41b7f6..c035ad936 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -1549,8 +1549,8 @@ CPacket* CRcvBuffer::getRcvReadyPacket(int32_t seqdistance) HLOGC(brlog.Debug, log << "getRcvReadyPacket: Sequence offset=" << seqdistance << " IS NOT RECEIVED."); return 0; } - IF_HEAVY_LOGGING(int nskipped = 0); + IF_HEAVY_LOGGING(int nskipped = 0); for (int i = m_iStartPos, n = m_iLastAckPos; i != n; i = shiftFwd(i)) { /* @@ -1559,8 +1559,8 @@ CPacket* CRcvBuffer::getRcvReadyPacket(int32_t seqdistance) if (m_pUnit[i] && m_pUnit[i]->m_iFlag == CUnit::GOOD) { HLOGC(brlog.Debug, - log << "getRcvReadyPacket: Found next packet seq=%" << m_pUnit[i]->m_Packet.getSeqNo() << " (" - << nskipped << " empty cells skipped)"); + log << "getRcvReadyPacket: Found next packet seq=%" << m_pUnit[i]->m_Packet.getSeqNo() << " (" + << nskipped << " empty cells skipped)"); return &m_pUnit[i]->m_Packet; } IF_HEAVY_LOGGING(++nskipped); @@ -1881,12 +1881,9 @@ void CRcvBuffer::setRcvTsbPdMode(const steady_clock::time_point& timebase, const m_tsbpd.setTsbPdMode(timebase, no_wrap_check, delay); } -bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, - int rtt, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase) +bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t timestamp_us, int rtt) { - return m_tsbpd.addDriftSample(timestamp_us, rtt, w_udrift, w_newtimebase); + return m_tsbpd.addDriftSample(timestamp_us, rtt); } int CRcvBuffer::readMsg(char* data, int len) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9bf02f216..2dc63d972 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -411,12 +411,8 @@ class CRcvBuffer /// Add packet timestamp for drift caclculation and compensation /// @param [in] timestamp packet time stamp - /// @param [out] w_udrift current drift value - /// @param [out] w_newtimebase current TSBPD base time - bool addRcvTsbPdDriftSample(uint32_t timestamp, - int rtt, - duration& w_udrift, - time_point& w_newtimebase); + /// @param [in] rtt RTT sample + bool addRcvTsbPdDriftSample(uint32_t timestamp, int rtt); #ifdef SRT_DEBUG_TSBPD_DRIFT void printDriftHistogram(int64_t iDrift); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a5fe649d8..ab1e7da9d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8324,17 +8324,14 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // srt_recvfile (which doesn't make any sense), you'll have a deadlock. if (m_config.bDriftTracer) { - steady_clock::duration udrift(0); - steady_clock::time_point newtimebase; - const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), - rtt, (udrift), (newtimebase)); + const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), rtt); #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { ScopedLock glock(s_UDTUnited.m_GlobControlLock); if (m_parent->m_GroupOf) { - m_parent->m_GroupOf->synchronizeDrift(this, udrift, newtimebase); + m_parent->m_GroupOf->synchronizeDrift(this); } } #endif diff --git a/srtcore/group.cpp b/srtcore/group.cpp index e835453ea..bee5c2beb 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2725,48 +2725,24 @@ const char* CUDTGroup::StateStr(CUDTGroup::GroupState st) return unknown; } -void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady_clock::time_point newtimebase) +void CUDTGroup::synchronizeDrift(const srt::CUDT* srcMember) { + SRT_ASSERT(srcMember != NULL); ScopedLock glock(m_GroupLock); - - bool wrap_period = false; - - bool anycheck = false; - - for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) - { - // Skip non-connected; these will be synchronized when ready - if (gi->laststatus != SRTS_CONNECTED) - continue; - - // Skip the entity that has reported this - if (cu == &gi->ps->core()) - continue; - - steady_clock::time_point this_timebase; - steady_clock::duration this_udrift(0); - bool wrp = false; - gi->ps->core().m_pRcvBuffer->getInternalTimeBase((this_timebase), (wrp), (this_udrift)); - - udrift = std::min(udrift, this_udrift); - steady_clock::time_point new_newtimebase = std::min(newtimebase, this_timebase); - if (new_newtimebase != newtimebase) - { - wrap_period = wrp; - } - newtimebase = new_newtimebase; - anycheck = true; - } - - if (!anycheck) + if (m_Group.size() <= 1) { HLOGC(grlog.Debug, log << "GROUP: synch uDRIFT NOT DONE, no other links"); return; } + steady_clock::time_point timebase; + steady_clock::duration udrift(0); + bool wrap_period = false; + srcMember->m_pRcvBuffer->getInternalTimeBase((timebase), (wrap_period), (udrift)); + HLOGC(grlog.Debug, - log << "GROUP: synch uDRIFT=" << FormatDuration(udrift) << " TB=" << FormatTime(newtimebase) << "(" - << (wrap_period ? "" : "NO ") << "wrap period)"); + log << "GROUP: synch uDRIFT=" << FormatDuration(udrift) << " TB=" << FormatTime(timebase) << "(" + << (wrap_period ? "" : "NO ") << "wrap period)"); // Now that we have the minimum timebase and drift calculated, apply this to every link, // INCLUDING THE REPORTER. @@ -2776,8 +2752,11 @@ void CUDTGroup::synchronizeDrift(CUDT* cu, steady_clock::duration udrift, steady // Skip non-connected; these will be synchronized when ready if (gi->laststatus != SRTS_CONNECTED) continue; + CUDT& member = gi->ps->core(); + if (srcMember == &member) + continue; - gi->ps->core().m_pRcvBuffer->applyGroupDrift(newtimebase, wrap_period, udrift); + member.m_pRcvBuffer->applyGroupDrift(timebase, wrap_period, udrift); } } diff --git a/srtcore/group.h b/srtcore/group.h index e4b2fb6ed..04c8e7e01 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -798,7 +798,10 @@ class CUDTGroup // Live state synchronization bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr); bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn); - void synchronizeDrift(srt::CUDT* cu, duration udrift, time_point newtimebase); + + /// @brief Synchronize TSBPD base time and clock drift among members using the @a srcMember as a reference. + /// @param srcMember a reference for synchronization. + void synchronizeDrift(const srt::CUDT* srcMember); void updateLatestRcv(srt::CUDTSocket*); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 54a8c5df5..9b75d595e 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -103,10 +103,7 @@ drift_logger g_drift_logger; #endif // SRT_DEBUG_TRACE_DRIFT -bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, - int usRTTSample, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase) +bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, int usRTTSample) { if (!m_bTsbPdMode) return false; @@ -149,9 +146,6 @@ bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase)); } - w_udrift = tdDrift; - w_newtimebase = m_tsTsbPdTimeBase; - #if SRT_DEBUG_TRACE_DRIFT g_drift_logger.trace(usPktTimestamp, usRTTSample, diff --git a/srtcore/tsbpd_time.h b/srtcore/tsbpd_time.h index b6cb770f5..dcaf05718 100644 --- a/srtcore/tsbpd_time.h +++ b/srtcore/tsbpd_time.h @@ -67,14 +67,9 @@ class CTsbpdTime /// /// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet. /// @param [in] usRTTSample RTT sample from an ACK-ACKACK pair. - /// @param [out] w_udrift Current clock drift value. - /// @param [out] w_newtimebase Current TSBPD base time. /// /// @return true if TSBPD base time has changed, false otherwise. - bool addDriftSample(uint32_t pktTimestamp, - int usRTTSample, - steady_clock::duration& w_udrift, - steady_clock::time_point& w_newtimebase); + bool addDriftSample(uint32_t pktTimestamp, int usRTTSample); /// @brief Handle timestamp of data packet when 32-bit integer carryover is about to happen. /// When packet timestamp approaches CPacket::MAX_TIMESTAMP, the TSBPD base time should be From 8b32f3734ff6af7cc7b0fef272591cb80a2d1aae Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 1 Oct 2021 14:25:02 +0200 Subject: [PATCH 170/683] [core] Fixed seqno validity check by group receiver (#2142) --- srtcore/common.h | 14 +++++++++----- srtcore/group.cpp | 39 ++++++++++++++++++++++++++++++++++++++- test/test_seqno.cpp | 9 +++------ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/srtcore/common.h b/srtcore/common.h index f6fdba44a..530759dde 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -634,12 +634,16 @@ class CSeqNo /// WITH A PRECONDITION that certainly @a seq1 is earlier than @a seq2. /// This can also include an enormously large distance between them, /// that is, exceeding the m_iSeqNoTH value (can be also used to test - /// if this distance is larger). Prior to calling this function the - /// caller must be certain that @a seq2 is a sequence coming from a - /// later time than @a seq1, and still, of course, this distance didn't - /// exceed m_iMaxSeqNo. + /// if this distance is larger). + /// Prior to calling this function the caller must be certain that + /// @a seq2 is a sequence coming from a later time than @a seq1, + /// and that the distance does not exceed m_iMaxSeqNo. inline static int seqlen(int32_t seq1, int32_t seq2) - {return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);} + { + SRT_ASSERT(seq1 >= 0 && seq1 <= m_iMaxSeqNo); + SRT_ASSERT(seq2 >= 0 && seq2 <= m_iMaxSeqNo); + return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2); + } /// This behaves like seq2 - seq1, with the precondition that the true /// distance between two sequence numbers never exceeds m_iSeqNoTH. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index bee5c2beb..f05f49369 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2148,6 +2148,43 @@ void CUDTGroup::updateWriteState() m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); } +/// Validate iPktSeqno is in range +/// (iBaseSeqno - m_iSeqNoTH/2; iBaseSeqno + m_iSeqNoTH). +/// +/// EXPECT_EQ(isValidSeqno(125, 124), true); // behind +/// EXPECT_EQ(isValidSeqno(125, 125), true); // behind +/// EXPECT_EQ(isValidSeqno(125, 126), true); // the next in order +/// +/// EXPECT_EQ(isValidSeqno(0, 0x3FFFFFFF - 2), true); // ahead, but ok. +/// EXPECT_EQ(isValidSeqno(0, 0x3FFFFFFF - 1), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF + 2, 0x7FFFFFFF), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF + 3, 0x7FFFFFFF), true); // ahead, but ok. +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF, 0x1FFFFFFF + 2), false); // too far (behind) +/// EXPECT_EQ(isValidSeqno(0x3FFFFFFF, 0x1FFFFFFF + 3), true); // behind, but ok +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x0FFFFFFF), true); // ahead, but ok +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x30000000 - 2), false); // too far ahead. +/// EXPECT_EQ(isValidSeqno(0x70000000, 0x30000000 - 3), true); // ahead, but ok +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0), true); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x7FFFFFFF), true); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000000), false); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000001), false); +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000002), true); // behind by 536870910 +/// EXPECT_EQ(isValidSeqno(0x0FFFFFFF, 0x70000003), true); +/// +/// @return false if @a iPktSeqno is not inside the valid range; otherwise true. +static bool isValidSeqno(int32_t iBaseSeqno, int32_t iPktSeqno) +{ + const int32_t iLenAhead = CSeqNo::seqlen(iBaseSeqno, iPktSeqno); + if (iLenAhead >= 0 && iLenAhead < CSeqNo::m_iSeqNoTH) + return true; + + const int32_t iLenBehind = CSeqNo::seqlen(iPktSeqno, iBaseSeqno); + if (iLenBehind >= 0 && iLenBehind < CSeqNo::m_iSeqNoTH / 2) + return true; + + return false; +} + // The "app reader" version of the reading function. // This reads the packets from every socket treating them as independent // and prepared to work with the application. Then packets are sorted out @@ -2407,7 +2444,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // embrace everything below. // We need to first qualify the sequence, just for a case - if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && abs(m_RcvBaseSeqNo - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, mctrl.pktseq)) { // This error should be returned if the link turns out // to be the only one, or set to the group data. diff --git a/test/test_seqno.cpp b/test/test_seqno.cpp index f3f50de62..f2e374f76 100644 --- a/test/test_seqno.cpp +++ b/test/test_seqno.cpp @@ -16,7 +16,6 @@ TEST(CSeqNo, constants) EXPECT_EQ(CSeqNo::m_iSeqNoTH, 0x3FFFFFFF); } - TEST(CSeqNo, seqcmp) { // Compare two seq#, considering the wraping. @@ -31,7 +30,6 @@ TEST(CSeqNo, seqcmp) EXPECT_EQ(CSeqNo::seqcmp(1, 0x7FFFFFFF), 0x7FFFFFFE); // 2147483646 } - TEST(CSeqNo, seqoff) { // seqoff: offset from the 2nd to the 1st seq# @@ -48,6 +46,9 @@ TEST(CSeqNo, seqlen) { EXPECT_EQ(CSeqNo::seqlen(125, 125), 1); EXPECT_EQ(CSeqNo::seqlen(125, 126), 2); + + EXPECT_EQ(CSeqNo::seqlen(2147483647, 0), 2); + EXPECT_EQ(CSeqNo::seqlen(0, 2147483647), -2147483648); } TEST(CUDT, getFlightSpan) @@ -74,7 +75,6 @@ TEST(CSeqNo, incseq) EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF), 0x40000000); } - TEST(CSeqNo, decseq) { // decseq: decrease the seq# by 1 @@ -84,7 +84,6 @@ TEST(CSeqNo, decseq) EXPECT_EQ(CSeqNo::decseq(0x40000000), 0x3FFFFFFF); } - TEST(CSeqNo, incseqint) { // incseq: increase the seq# by 1 @@ -98,7 +97,6 @@ TEST(CSeqNo, incseqint) EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 0x40000001), 0x00000000); } - TEST(CSeqNo, decseqint) { // decseq: decrease the seq# by 1 @@ -107,4 +105,3 @@ TEST(CSeqNo, decseqint) EXPECT_EQ(CSeqNo::decseq(0, 1), 0x7FFFFFFF); EXPECT_EQ(CSeqNo::decseq(0x40000000, 1), 0x3FFFFFFF); } - From 1a2aa05c159e72bf2530a7f25cb7a7d6891272df Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 21 Sep 2021 16:09:45 +0200 Subject: [PATCH 171/683] [apps] Print SRT version and clock type --- apps/apputil.cpp | 33 +++++++++++++++++++++++++++++++++ apps/apputil.hpp | 3 +++ apps/srt-file-transmit.cpp | 10 ++-------- apps/srt-live-transmit.cpp | 10 ++-------- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 611b99976..1389b748e 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -684,3 +684,36 @@ SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) return SRTSTATS_PROFMAT_INVALID; } + +const char* SRTClockTypeStr() +{ + const int clock_type = srt_clock_type(); + + switch (clock_type) + { + case SRT_SYNC_CLOCK_STDCXX_STEADY: + return "CXX11_STEADY"; + case SRT_SYNC_CLOCK_GETTIME_MONOTONIC: + return "GETTIME_MONOTONIC"; + case SRT_SYNC_CLOCK_WINQPC: + return "WIN_QPC"; + case SRT_SYNC_CLOCK_MACH_ABSTIME: + return "MACH_ABSTIME"; + case SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY: + return "POSIX_GETTIMEOFDAY"; + default: + break; + } + + return "UNKNOWN VALUE"; +} + +void PrintLibVersion() +{ + cerr << "Built with SRT Library version: " << SRT_VERSION << endl; + const uint32_t srtver = srt_getversion(); + const int major = srtver / 0x10000; + const int minor = (srtver / 0x100) % 0x100; + const int patch = srtver % 0x100; + cerr << "SRT Library version: " << major << "." << minor << "." << patch << ", clock type: " << SRTClockTypeStr() << endl; +} diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 5bac461e0..782412815 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -419,5 +419,8 @@ extern std::vector> g_SrtStatsTable; std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); +const char* SRTClockTypeStr(); +void PrintLibVersion(); + #endif // INC_SRT_APPCOMMON_H diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index d3ea92092..327ad6809 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -52,7 +52,6 @@ void OnINT_ForceExit(int) interrupt = true; } - struct FileTransmitConfig { unsigned long chunk_size; @@ -144,12 +143,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) if (print_help) { cout << "SRT sample application to transmit files.\n"; - cerr << "Built with SRT Library version: " << SRT_VERSION << endl; - const uint32_t srtver = srt_getversion(); - const int major = srtver / 0x10000; - const int minor = (srtver / 0x100) % 0x100; - const int patch = srtver % 0x100; - cerr << "SRT Library version: " << major << "." << minor << "." << patch << endl; + PrintLibVersion(); cerr << "Usage: srt-file-transmit [options] \n"; cerr << "\n"; @@ -182,7 +176,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) if (Option(params, false, o_version)) { - cerr << "SRT Library version: " << SRT_VERSION << endl; + PrintLibVersion(); return 2; } diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 9aad3209c..6d5ee18de 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -164,7 +164,6 @@ void PrintOptionHelp(const OptionName& opt_names, const string &value, const str cerr << "\t- " << desc << "\n"; } - int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) { const OptionName @@ -269,12 +268,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) } cout << "SRT sample application to transmit live streaming.\n"; - cerr << "Built with SRT Library version: " << SRT_VERSION << endl; - const uint32_t srtver = srt_getversion(); - const int major = srtver / 0x10000; - const int minor = (srtver / 0x100) % 0x100; - const int patch = srtver % 0x100; - cerr << "SRT Library version: " << major << "." << minor << "." << patch << endl; + PrintLibVersion(); cerr << "Usage: srt-live-transmit [options] \n"; cerr << "\n"; #ifndef _WIN32 @@ -313,7 +307,7 @@ int parse_args(LiveTransmitConfig &cfg, int argc, char** argv) if (print_version) { - cerr << "SRT Library version: " << SRT_VERSION << endl; + PrintLibVersion(); return 2; } From 0c604c1ec51f8c4ed7918d664abf5dad7a0126bf Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 10 Sep 2021 15:56:16 +0200 Subject: [PATCH 172/683] [core] Moved SND and RCV buffers into the 'srt' namespace --- srtcore/buffer.cpp | 23 ++++++++++-------- srtcore/buffer.h | 60 ++++++++++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index c035ad936..329b054a0 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -59,10 +59,11 @@ modified by #include "core.h" // provides some constants #include "logging.h" +namespace srt { + using namespace std; using namespace srt_logging; -using namespace srt; -using namespace srt::sync; +using namespace sync; // You can change this value at build config by using "ENFORCE" options. #if !defined(SRT_MAVG_SAMPLING_RATE) @@ -318,7 +319,7 @@ void CSndBuffer::updateInputRate(const steady_clock::time_point& time, int pkts, if (early_update || period_us > m_InRatePeriod) { // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * srt::CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount @@ -411,7 +412,7 @@ steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& bloc return block.m_tsOriginTime; } -int CSndBuffer::readData(srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read if (m_pCurrBlock == m_pLastBlock) @@ -512,7 +513,7 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) { int32_t& msgno_bitset = w_packet.m_iMsgNo; @@ -605,7 +606,7 @@ int CSndBuffer::readData(const int offset, srt::CPacket& w_packet, steady_clock: return readlen; } -srt::sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) +sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset) { ScopedLock bufferguard(m_BufLock); const Block* p = m_pFirstBlock; @@ -935,7 +936,7 @@ int CRcvBuffer::readBuffer(char* data, int len) return -1; } - const srt::CPacket& pkt = m_pUnit[p]->m_Packet; + const CPacket& pkt = m_pUnit[p]->m_Packet; if (bTsbPdEnabled) { @@ -1004,7 +1005,7 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) continue; } - const srt::CPacket& pkt = m_pUnit[p]->m_Packet; + const CPacket& pkt = m_pUnit[p]->m_Packet; #if ENABLE_LOGGING trace_seq = pkt.getSeqNo(); @@ -1476,7 +1477,7 @@ bool CRcvBuffer::isRcvDataReady(steady_clock::time_point& w_tsbpdtime, int32_t& if (m_tsbpd.isEnabled()) { - const srt::CPacket* pkt = getRcvReadyPacket(seqdistance); + const CPacket* pkt = getRcvReadyPacket(seqdistance); if (!pkt) { HLOGC(brlog.Debug, log << "isRcvDataReady: packet NOT extracted."); @@ -1613,7 +1614,7 @@ void CRcvBuffer::reportBufferStats() const uint64_t lower_time = low_ts; if (lower_time > upper_time) - upper_time += uint64_t(srt::CPacket::MAX_TIMESTAMP) + 1; + upper_time += uint64_t(CPacket::MAX_TIMESTAMP) + 1; int32_t timespan = upper_time - lower_time; int seqspan = 0; @@ -2287,3 +2288,5 @@ bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) return found; } + +} // namespace srt diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 2dc63d972..8756a49d1 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -71,10 +71,12 @@ modified by // a +% b : shift a by b // a == b : equality is same as for just numbers +namespace srt { + /// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND) class AvgBufSize { - typedef srt::sync::steady_clock::time_point time_point; + typedef sync::steady_clock::time_point time_point; public: AvgBufSize() @@ -102,8 +104,8 @@ class AvgBufSize class CSndBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; public: // XXX There's currently no way to access the socket ID set for @@ -140,21 +142,19 @@ class CSndBuffer int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] data the pointer to the data position. - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [in] kflags Odd|Even crypto key flag + /// @param [out] w_packet data packet buffer to fill. + /// @param [out] w_origintime origin time stamp of the message. + /// @param [in] kflags Odd|Even crypto key flag. /// @return Actual length of data read. - int readData(srt::CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); /// Find data position to pack a DATA packet for a retransmission. - /// @param [out] data the pointer to the data position. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] msgno message number of the packet. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message + /// @param [out] w_packet data packet buffer to fill. + /// @param [out] w_origintime origin time stamp of the message + /// @param [out] w_msglen length of the message /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). - int readData(const int offset, srt::CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) @@ -207,7 +207,7 @@ class CSndBuffer static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - srt::sync::Mutex m_BufLock; // used to synchronize buffer operation + sync::Mutex m_BufLock; // used to synchronize buffer operation struct Block { @@ -274,8 +274,8 @@ class CSndBuffer class CRcvBuffer { - typedef srt::sync::steady_clock::time_point time_point; - typedef srt::sync::steady_clock::duration duration; + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; public: // XXX There's currently no way to access the socket ID set for @@ -288,7 +288,7 @@ class CRcvBuffer /// Construct the buffer. /// @param [in] queue CUnitQueue that actually holds the units (packets) /// @param [in] bufsize_pkts in units (packets) - CRcvBuffer(srt::CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); + CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE); ~CRcvBuffer(); public: @@ -296,7 +296,7 @@ class CRcvBuffer /// @param [in] unit pointer to a data unit containing new packet /// @param [in] offset offset from last ACK point. /// @return 0 is success, -1 if data is repeated. - int addData(srt::CUnit* unit, int offset); + int addData(CUnit* unit, int offset); /// Read data into a user buffer. /// @param [in] data pointer to user buffer. @@ -402,7 +402,7 @@ class CRcvBuffer bool isRcvDataReady(); bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; } - srt::CPacket* getRcvReadyPacket(int32_t seqdistance); + CPacket* getRcvReadyPacket(int32_t seqdistance); /// Set TimeStamp-Based Packet Delivery Rx Mode /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay @@ -464,7 +464,7 @@ class CRcvBuffer /// data. size_t freeUnitAt(size_t p) { - srt::CUnit* u = m_pUnit[p]; + CUnit* u = m_pUnit[p]; m_pUnit[p] = NULL; size_t rmbytes = u->m_Packet.getLength(); m_pUnitQueue->makeUnitFree(u); @@ -531,9 +531,9 @@ class CRcvBuffer } private: - srt::CUnit** m_pUnit; // Array of pointed units collected in the buffer + CUnit** m_pUnit; // Array of pointed units collected in the buffer const int m_iSize; // Size of the internal array of CUnit* items - srt::CUnitQueue* m_pUnitQueue; // the shared unit queue + CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartPos; // HEAD: first packet available for reading int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable @@ -546,15 +546,15 @@ class CRcvBuffer // up to which data are already retrieved; // in message reading mode it's unused and always 0) - srt::sync::Mutex m_BytesCountLock; // used to protect counters operations - int m_iBytesCount; // Number of payload bytes in the buffer - int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer - int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer - unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation + sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer + int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation - srt::CTsbpdTime m_tsbpd; + CTsbpdTime m_tsbpd; - AvgBufSize m_mavg; + AvgBufSize m_mavg; private: CRcvBuffer(); @@ -562,4 +562,6 @@ class CRcvBuffer CRcvBuffer& operator=(const CRcvBuffer&); }; +} // namespace srt + #endif From 5b0811ca33cecb9cc2d1088ca6158277c599b317 Mon Sep 17 00:00:00 2001 From: quink-black Date: Mon, 4 Oct 2021 21:01:55 +0800 Subject: [PATCH 173/683] [core] Replaced global CUDTUnited with static local variable (#1913) A static local variable has the benefits of lazy initialization and avoids the initialization order problem of global variables. --- srtcore/api.cpp | 132 ++++++++++++++-------------- srtcore/core.cpp | 147 ++++++++++++++++++------------- srtcore/core.h | 5 +- srtcore/group.cpp | 162 +++++++++++++++++------------------ srtcore/group.h | 4 +- srtcore/queue.cpp | 2 +- test/test_fec_rebuilding.cpp | 4 +- 7 files changed, 241 insertions(+), 215 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 71386a566..38b86cf8c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -846,7 +846,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& p // static forwarder int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { - return s_UDTUnited.installAcceptHook(lsn, hook, opaq); + return uglobal().installAcceptHook(lsn, hook, opaq); } int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) @@ -867,7 +867,7 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { - return s_UDTUnited.installConnectHook(lsn, hook, opaq); + return uglobal().installConnectHook(lsn, hook, opaq); } int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) @@ -3210,22 +3210,22 @@ void* srt::CUDTUnited::garbageCollect(void* p) int srt::CUDT::startup() { - return s_UDTUnited.startup(); + return uglobal().startup(); } int srt::CUDT::cleanup() { - return s_UDTUnited.cleanup(); + return uglobal().cleanup(); } SRTSOCKET srt::CUDT::socket() { - if (!s_UDTUnited.m_bGCStatus) - s_UDTUnited.startup(); + if (!uglobal().m_bGCStatus) + uglobal().startup(); try { - return s_UDTUnited.newSocket(); + return uglobal().newSocket(); } catch (const CUDTException& e) { @@ -3264,21 +3264,21 @@ srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) // [[using locked(s_UDTUnited.m_GlobControlLock)]] srt::CUDTGroup& srt::CUDT::newGroup(const int type) { - const SRTSOCKET id = s_UDTUnited.generateSocketID(true); + const SRTSOCKET id = uglobal().generateSocketID(true); // Now map the group - return s_UDTUnited.addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); + return uglobal().addGroup(id, SRT_GROUP_TYPE(type)).set_id(id); } SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() - if (!s_UDTUnited.m_bGCStatus) - s_UDTUnited.startup(); + if (!uglobal().m_bGCStatus) + uglobal().startup(); try { - srt::sync::ScopedLock globlock (s_UDTUnited.m_GlobControlLock); + srt::sync::ScopedLock globlock (uglobal().m_GlobControlLock); return newGroup(gt).id(); // Note: potentially, after this function exits, the group // could be deleted, immediately, from a separate thread (tho @@ -3309,8 +3309,8 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) return APIError(MJ_NOTSUP, MN_INVAL, 0); // Find the socket and the group - CUDTSocket* s = s_UDTUnited.locateSocket(socket); - CUDTUnited::GroupKeeper k (s_UDTUnited, group, s_UDTUnited.ERH_RETURN); + CUDTSocket* s = uglobal().locateSocket(socket); + CUDTUnited::GroupKeeper k (uglobal(), group, CUDTUnited::ERH_RETURN); if (!s || !k.group) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3331,7 +3331,7 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) } ScopedLock cg (s->m_ControlLock); - ScopedLock cglob (s_UDTUnited.m_GlobControlLock); + ScopedLock cglob (uglobal().m_GlobControlLock); if (g->closing()) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3355,7 +3355,7 @@ int srt::CUDT::addSocketToGroup(SRTSOCKET socket, SRTSOCKET group) // groups. int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) { - CUDTSocket* s = s_UDTUnited.locateSocket(socket); + CUDTSocket* s = uglobal().locateSocket(socket); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3363,7 +3363,7 @@ int srt::CUDT::removeSocketFromGroup(SRTSOCKET socket) return APIError(MJ_NOTSUP, MN_INVAL, 0); ScopedLock cg (s->m_ControlLock); - ScopedLock glob_grd (s_UDTUnited.m_GlobControlLock); + ScopedLock glob_grd (uglobal().m_GlobControlLock); s->removeFromGroup(false); return 0; } @@ -3403,8 +3403,8 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) { // Lock this for the whole function as we need the group // to persist the call. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); - CUDTSocket* s = s_UDTUnited.locateSocket_LOCKED(socket); + ScopedLock glock (uglobal().m_GlobControlLock); + CUDTSocket* s = uglobal().locateSocket_LOCKED(socket); if (!s || !s->m_GroupOf) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3418,7 +3418,7 @@ int srt::CUDT::configureGroup(SRTSOCKET groupid, const char* str) return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTUnited::GroupKeeper k (s_UDTUnited, groupid, s_UDTUnited.ERH_RETURN); + CUDTUnited::GroupKeeper k (uglobal(), groupid, CUDTUnited::ERH_RETURN); if (!k.group) { return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3434,7 +3434,7 @@ int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTUnited::GroupKeeper k (s_UDTUnited, groupid, s_UDTUnited.ERH_RETURN); + CUDTUnited::GroupKeeper k (uglobal(), groupid, CUDTUnited::ERH_RETURN); if (!k.group) { return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3458,11 +3458,11 @@ int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) // This is a user error. return APIError(MJ_NOTSUP, MN_INVAL, 0); } - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); - return s_UDTUnited.bind(s, sa); + return uglobal().bind(s, sa); } catch (const CUDTException& e) { @@ -3485,11 +3485,11 @@ int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_INVAL, 0); - return s_UDTUnited.bind(s, udpsock); + return uglobal().bind(s, udpsock); } catch (const CUDTException& e) { @@ -3511,7 +3511,7 @@ int srt::CUDT::listen(SRTSOCKET u, int backlog) { try { - return s_UDTUnited.listen(u, backlog); + return uglobal().listen(u, backlog); } catch (const CUDTException& e) { @@ -3533,7 +3533,7 @@ SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners [], int lsize, int64_ { try { - return s_UDTUnited.accept_bond(listeners, lsize, msTimeOut); + return uglobal().accept_bond(listeners, lsize, msTimeOut); } catch (const CUDTException& e) { @@ -3558,7 +3558,7 @@ SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) { try { - return s_UDTUnited.accept(u, addr, addrlen); + return uglobal().accept(u, addr, addrlen); } catch (const CUDTException& e) { @@ -3584,7 +3584,7 @@ int srt::CUDT::connect( { try { - return s_UDTUnited.connect(u, name, tname, namelen); + return uglobal().connect(u, name, tname, namelen); } catch (const CUDTException& e) { @@ -3617,8 +3617,8 @@ int srt::CUDT::connectLinks(SRTSOCKET grp, try { - CUDTUnited::GroupKeeper k(s_UDTUnited, grp, s_UDTUnited.ERH_THROW); - return s_UDTUnited.groupConnect(k.group, targets, arraysize); + CUDTUnited::GroupKeeper k(uglobal(), grp, CUDTUnited::ERH_THROW); + return uglobal().groupConnect(k.group, targets, arraysize); } catch (CUDTException& e) { @@ -3642,7 +3642,7 @@ int srt::CUDT::connect( { try { - return s_UDTUnited.connect(u, name, namelen, forced_isn); + return uglobal().connect(u, name, namelen, forced_isn); } catch (const CUDTException &e) { @@ -3664,7 +3664,7 @@ int srt::CUDT::close(SRTSOCKET u) { try { - return s_UDTUnited.close(u); + return uglobal().close(u); } catch (const CUDTException& e) { @@ -3682,7 +3682,7 @@ int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { - s_UDTUnited.getpeername(u, name, namelen); + uglobal().getpeername(u, name, namelen); return 0; } catch (const CUDTException& e) @@ -3701,7 +3701,7 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { - s_UDTUnited.getsockname(u, name, namelen); + uglobal().getsockname(u, name, namelen); return 0; } catch (const CUDTException& e) @@ -3729,13 +3729,13 @@ int srt::CUDT::getsockopt( #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } #endif - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.getOpt(optname, (pw_optval), (*pw_optlen)); return 0; } @@ -3761,13 +3761,13 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); return 0; } #endif - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.setOpt(optname, optval, optlen); return 0; } @@ -3810,12 +3810,12 @@ int srt::CUDT::sendmsg2( #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k (s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k (uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); } #endif - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().sendmsg2(buf, len, (w_m)); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().sendmsg2(buf, len, (w_m)); } catch (const CUDTException& e) { @@ -3855,12 +3855,12 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) #if ENABLE_EXPERIMENTAL_BONDING if (u & SRTGROUP_MASK) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); } #endif - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().recvmsg2(buf, len, (w_m)); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().recvmsg2(buf, len, (w_m)); } catch (const CUDTException& e) { @@ -3879,7 +3879,7 @@ int64_t srt::CUDT::sendfile( { try { - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); return udt.sendfile(ifs, offset, size, block); } catch (const CUDTException& e) @@ -3903,7 +3903,7 @@ int64_t srt::CUDT::recvfile( { try { - return s_UDTUnited.locateSocket(u, CUDTUnited::ERH_THROW)->core().recvfile(ofs, offset, size, block); + return uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core().recvfile(ofs, offset, size, block); } catch (const CUDTException& e) { @@ -3931,7 +3931,7 @@ int srt::CUDT::select( try { - return s_UDTUnited.select(readfds, writefds, exceptfds, timeout); + return uglobal().select(readfds, writefds, exceptfds, timeout); } catch (const CUDTException& e) { @@ -3963,7 +3963,7 @@ int srt::CUDT::selectEx( try { - return s_UDTUnited.selectEx(fds, readfds, writefds, exceptfds, msTimeOut); + return uglobal().selectEx(fds, readfds, writefds, exceptfds, msTimeOut); } catch (const CUDTException& e) { @@ -3985,7 +3985,7 @@ int srt::CUDT::epoll_create() { try { - return s_UDTUnited.epoll_create(); + return uglobal().epoll_create(); } catch (const CUDTException& e) { @@ -4003,7 +4003,7 @@ int srt::CUDT::epoll_clear_usocks(int eid) { try { - return s_UDTUnited.epoll_clear_usocks(eid); + return uglobal().epoll_clear_usocks(eid); } catch (const CUDTException& e) { @@ -4021,7 +4021,7 @@ int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* even { try { - return s_UDTUnited.epoll_add_usock(eid, u, events); + return uglobal().epoll_add_usock(eid, u, events); } catch (const CUDTException& e) { @@ -4039,7 +4039,7 @@ int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* even { try { - return s_UDTUnited.epoll_add_ssock(eid, s, events); + return uglobal().epoll_add_ssock(eid, s, events); } catch (const CUDTException& e) { @@ -4058,7 +4058,7 @@ int srt::CUDT::epoll_update_usock( { try { - return s_UDTUnited.epoll_add_usock(eid, u, events); + return uglobal().epoll_add_usock(eid, u, events); } catch (const CUDTException& e) { @@ -4077,7 +4077,7 @@ int srt::CUDT::epoll_update_ssock( { try { - return s_UDTUnited.epoll_update_ssock(eid, s, events); + return uglobal().epoll_update_ssock(eid, s, events); } catch (const CUDTException& e) { @@ -4096,7 +4096,7 @@ int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { - return s_UDTUnited.epoll_remove_usock(eid, u); + return uglobal().epoll_remove_usock(eid, u); } catch (const CUDTException& e) { @@ -4114,7 +4114,7 @@ int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { - return s_UDTUnited.epoll_remove_ssock(eid, s); + return uglobal().epoll_remove_ssock(eid, s); } catch (const CUDTException& e) { @@ -4138,7 +4138,7 @@ int srt::CUDT::epoll_wait( { try { - return s_UDTUnited.epoll_ref().wait( + return uglobal().epoll_ref().wait( eid, readfds, writefds, msTimeOut, lrfds, lwfds); } catch (const CUDTException& e) @@ -4161,7 +4161,7 @@ int srt::CUDT::epoll_uwait( { try { - return s_UDTUnited.epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); + return uglobal().epoll_uwait(eid, fdsSet, fdsSize, msTimeOut); } catch (const CUDTException& e) { @@ -4181,7 +4181,7 @@ int32_t srt::CUDT::epoll_set( { try { - return s_UDTUnited.epoll_set(eid, flags); + return uglobal().epoll_set(eid, flags); } catch (const CUDTException& e) { @@ -4199,7 +4199,7 @@ int srt::CUDT::epoll_release(const int eid) { try { - return s_UDTUnited.epoll_release(eid); + return uglobal().epoll_release(eid); } catch (const CUDTException& e) { @@ -4227,7 +4227,7 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta try { - CUDT& udt = s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.bstats(perf, clear, instantaneous); return 0; } @@ -4248,7 +4248,7 @@ int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->bstatsSocket(perf, clear); return 0; } @@ -4271,7 +4271,7 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) { try { - return &s_UDTUnited.locateSocket(u, s_UDTUnited.ERH_THROW)->core(); + return &uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); } catch (const CUDTException& e) { @@ -4290,8 +4290,8 @@ srt::CUDT* srt::CUDT::getUDTHandle(SRTSOCKET u) vector srt::CUDT::existingSockets() { vector out; - for (CUDTUnited::sockets_t::iterator i = s_UDTUnited.m_Sockets.begin(); - i != s_UDTUnited.m_Sockets.end(); ++i) + for (CUDTUnited::sockets_t::iterator i = uglobal().m_Sockets.begin(); + i != uglobal().m_Sockets.end(); ++i) { out.push_back(i->first); } @@ -4305,11 +4305,11 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) #if ENABLE_EXPERIMENTAL_BONDING if (isgroup(u)) { - CUDTUnited::GroupKeeper k(s_UDTUnited, u, s_UDTUnited.ERH_THROW); + CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->getStatus(); } #endif - return s_UDTUnited.getStatus(u); + return uglobal().getStatus(u); } catch (const CUDTException& e) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ab1e7da9d..53e4a28d5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -70,6 +70,11 @@ modified by #include "logging_api.h" // Required due to containing extern srt_logger_config #include "logger_defs.h" +#if !HAVE_CXX11 +// for pthread_once +#include +#endif + // Again, just in case when some "smart guy" provided such a global macro #ifdef min #undef min @@ -83,10 +88,6 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -namespace srt { - CUDTUnited CUDT::s_UDTUnited; -} - const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; @@ -222,6 +223,32 @@ const SrtOptionAction s_sockopt_action; } // namespace srt +#if HAVE_CXX11 + +CUDTUnited& srt::CUDT::uglobal() +{ + static CUDTUnited instance; + return instance; +} + +#else // !HAVE_CXX11 + +static pthread_once_t s_UDTUnitedOnce = PTHREAD_ONCE_INIT; + +static CUDTUnited *getInstance() +{ + static CUDTUnited instance; + return &instance; +} + +CUDTUnited& srt::CUDT::uglobal() +{ + // We don't want lock each time, pthread_once can be faster than mutex. + pthread_once(&s_UDTUnitedOnce, reinterpret_cast(getInstance)); + return *getInstance(); +} + +#endif void srt::CUDT::construct() { @@ -512,7 +539,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) break; case SRTO_STATE: - *(int32_t *)optval = s_UDTUnited.getStatus(m_SocketID); + *(int32_t *)optval = uglobal().getStatus(m_SocketID); optlen = sizeof(int32_t); break; @@ -1692,7 +1719,7 @@ bool srt::CUDT::createSrtHandshake( if (have_group) { // NOTE: See information about mutex ordering in api.h - ScopedLock gdrg (s_UDTUnited.m_GlobControlLock); + ScopedLock gdrg (uglobal().m_GlobControlLock); if (!m_parent->m_GroupOf) { // This may only happen if since last check of m_GroupOf pointer the socket was removed @@ -3039,7 +3066,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A return false; } - ScopedLock guard_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock guard_group_existence (uglobal().m_GlobControlLock); if (m_SrtHsSide == HSD_INITIATOR) { @@ -3161,7 +3188,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // it right now so there's no need to lock s->m_ControlLock. // Check if there exists a group that this one is a peer of. - CUDTGroup* gp = s_UDTUnited.findPeerGroup_LOCKED(peergroup); + CUDTGroup* gp = uglobal().findPeerGroup_LOCKED(peergroup); bool was_empty = true; if (gp) { @@ -3192,7 +3219,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. - s_UDTUnited.deleteGroup_LOCKED(gp); + uglobal().deleteGroup_LOCKED(gp); return -1; } @@ -4569,7 +4596,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, { #if ENABLE_EXPERIMENTAL_BONDING - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -4619,7 +4646,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // the socket could have been started removal before this function // has started. Do a sanity check before you continue with the // connection process. - CUDTSocket* s = s_UDTUnited.locateSocket(m_SocketID); + CUDTSocket* s = uglobal().locateSocket(m_SocketID); if (s) { // The socket could be closed at this very moment. @@ -4676,7 +4703,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, //int token = -1; #if ENABLE_EXPERIMENTAL_BONDING { - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -4705,7 +4732,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, s->m_Status = SRTS_CONNECTED; // acknowledde any waiting epolls to write - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_CONNECT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_CONNECT, true); CGlobEvent::triggerEvent(); @@ -5150,7 +5177,7 @@ void * srt::CUDT::tsbpd(void *param) // which will ensure that the group will not be physically // deleted until this thread exits. // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!! - CUDTUnited::GroupKeeper gkeeper (self->s_UDTUnited, self->m_parent); + CUDTUnited::GroupKeeper gkeeper (self->uglobal(), self->m_parent); #endif UniqueLock recv_lock (self->m_RecvLock); @@ -5268,7 +5295,7 @@ void * srt::CUDT::tsbpd(void *param) /* * Set EPOLL_IN to wakeup any thread waiting on epoll */ - self->s_UDTUnited.m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); + self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); #if ENABLE_EXPERIMENTAL_BONDING // If this is NULL, it means: // - the socket never was a group member @@ -5545,7 +5572,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& { #if ENABLE_EXPERIMENTAL_BONDING - ScopedLock cl (s_UDTUnited.m_GlobControlLock); + ScopedLock cl (uglobal().m_GlobControlLock); CUDTGroup* g = m_parent->m_GroupOf; if (g) { @@ -5902,13 +5929,13 @@ bool srt::CUDT::closeInternal() // Make a copy under a lock because other thread might access it // at the same time. - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); set epollid = m_sPollID; - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); // trigger any pending IO events. HLOGC(smlog.Debug, log << "close: SETTING ERR readiness on E" << Printable(epollid) << " of @" << m_SocketID); - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); // then remove itself from all epoll monitoring int no_events = 0; for (set::iterator i = epollid.begin(); i != epollid.end(); ++i) @@ -5916,7 +5943,7 @@ bool srt::CUDT::closeInternal() HLOGC(smlog.Debug, log << "close: CLEARING subscription on E" << (*i) << " of @" << m_SocketID); try { - s_UDTUnited.m_EPoll.update_usock(*i, m_SocketID, &no_events); + uglobal().m_EPoll.update_usock(*i, m_SocketID, &no_events); } catch (...) { @@ -5933,9 +5960,9 @@ bool srt::CUDT::closeInternal() // IMPORTANT: there's theoretically little time between setting ERR readiness // and unsubscribing, however if there's an application waiting on this event, // it should be informed before this below instruction locks the epoll mutex. - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.clear(); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); // XXX What's this, could any of the above actions make it !m_bOpened? if (!m_bOpened) @@ -6128,7 +6155,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } if ((res <= 0) && (m_config.iRcvTimeOut >= 0)) @@ -6515,7 +6542,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) if (sndBuffersLeft() < 1) // XXX Not sure if it should test if any space in the buffer, or as requried. { // write is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); } } @@ -6638,7 +6665,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } if (res == 0) @@ -6679,7 +6706,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); // Forced to return 0 instead of throwing exception, in case of AGAIN/READ if (!by_exception) return 0; @@ -6700,7 +6727,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING @@ -6819,7 +6846,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } // Shut up EPoll if no more messages in non-blocking mode - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } // Unblock when required @@ -6948,7 +6975,7 @@ int64_t srt::CUDT::sendfile(fstream &ifs, int64_t &offset, int64_t size, int blo if (sndBuffersLeft() <= 0) { // write is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, false); } } @@ -7074,7 +7101,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo if (!m_pRcvBuffer->isRcvDataReady()) { // read is not available any more - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); } return size - torecv; @@ -7745,7 +7772,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // can be either never set, already reset, or ever set // and possibly dangling. The re-check after lock eliminates // the dangling case. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); // Note that updateLatestRcv will lock m_GroupOf->m_GroupLock, // but this is an intended order. @@ -7797,13 +7824,13 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // (4) receive thread: receive data and set SRT_EPOLL_IN to true // (5) user thread: set SRT_EPOLL_IN to false // 4. so , m_RecvLock must be used here to protect epoll event - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { // See above explanation for double-checking - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { @@ -7953,7 +7980,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) m_pSndBuffer->ackData(offset); // acknowledde any waiting epolls to write - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); CGlobEvent::triggerEvent(); } @@ -7962,7 +7989,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) { // m_RecvAckLock is ordered AFTER m_GlobControlLock, so this can only // be done now that m_RecvAckLock is unlocked. - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { HLOGC(inlog.Debug, log << "ACK: acking group sender buffer for #" << msgno_at_last_acked_seq); @@ -8111,7 +8138,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { // Will apply m_GroupLock, ordered after m_GlobControlLock. @@ -8328,7 +8355,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr #if ENABLE_EXPERIMENTAL_BONDING if (drift_updated && m_parent->m_GroupOf) { - ScopedLock glock(s_UDTUnited.m_GlobControlLock); + ScopedLock glock(uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { m_parent->m_GroupOf->synchronizeDrift(this); @@ -8827,7 +8854,7 @@ void srt::CUDT::updateAfterSrtHandshake(int hsv) if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); grpspec = m_parent->m_GroupOf ? " group=$" + Sprint(m_parent->m_GroupOf->id()) : string(); @@ -9272,7 +9299,7 @@ void srt::CUDT::processClose() // Signal the sender and recver if they are waiting for data. releaseSynch(); // Unblock any call so they learn the connection_broken error - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_ERR, true); HLOGP(smlog.Debug, "processClose: triggering timer event to spread the bad news"); CGlobEvent::triggerEvent(); @@ -9547,7 +9574,7 @@ int srt::CUDT::processData(CUnit* in_unit) // reception sequence pointer stating that this link is not receiving. if (m_parent->m_GroupOf) { - ScopedLock protect_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock protect_group_existence (uglobal().m_GlobControlLock); groups::SocketData* gi = m_parent->m_GroupMemberData; // This check is needed as after getting the lock the socket @@ -10563,7 +10590,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { int error = SRT_REJ_UNKNOWN; CUDT* acpu = NULL; - int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); + int result = uglobal().newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); // This is listener - m_RejectReason need not be set // because listener has no functionality of giving the app @@ -10703,7 +10730,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // Note: not using SRT_EPOLL_CONNECT symbol because this is a procedure // executed for the accepted socket. - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } } LOGC(cnlog.Note, log << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); @@ -11003,7 +11030,7 @@ void srt::CUDT::checkTimers() #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) { - ScopedLock glock (s_UDTUnited.m_GlobControlLock); + ScopedLock glock (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { // Pass socket ID because it's about changing group socket data @@ -11023,7 +11050,7 @@ void srt::CUDT::updateBrokenConnection() m_bClosing = true; releaseSynch(); // app can call any UDT API to learn the connection_broken error - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); CGlobEvent::triggerEvent(); } @@ -11034,7 +11061,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) #if ENABLE_EXPERIMENTAL_BONDING bool pending_broken = false; { - ScopedLock guard_group_existence (s_UDTUnited.m_GlobControlLock); + ScopedLock guard_group_existence (uglobal().m_GlobControlLock); if (m_parent->m_GroupOf) { token = m_parent->m_GroupMemberData->token; @@ -11066,7 +11093,7 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // existence of the group will not be changed during // the operation. The attempt of group deletion will // have to wait until this operation completes. - ScopedLock lock(s_UDTUnited.m_GlobControlLock); + ScopedLock lock(uglobal().m_GlobControlLock); CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { @@ -11079,8 +11106,8 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // explicitly, otherwise they will never be deleted. if (pending_broken) { - // XXX This somehow can cause a deadlock, even without GlobControlLock - // s_UDTUnited.close(m_parent); + // XXX This somehow can cause a deadlock + // uglobal()->close(m_parent); m_parent->setBrokenClosed(); } #endif @@ -11088,9 +11115,9 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) void srt::CUDT::addEPoll(const int eid) { - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.insert(eid); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); if (!stillConnected()) return; @@ -11098,13 +11125,13 @@ void srt::CUDT::addEPoll(const int eid) enterCS(m_RecvLock); if (m_pRcvBuffer->isRcvDataReady()) { - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } leaveCS(m_RecvLock); if (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize()) { - s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_OUT, true); } } @@ -11114,14 +11141,14 @@ void srt::CUDT::removeEPollEvents(const int eid) // since this happens after the epoll ID has been removed, they cannot be set again set remove; remove.insert(eid); - s_UDTUnited.m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); + uglobal().m_EPoll.update_events(m_SocketID, remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } void srt::CUDT::removeEPollID(const int eid) { - enterCS(s_UDTUnited.m_EPoll.m_EPollLock); + enterCS(uglobal().m_EPoll.m_EPollLock); m_sPollID.erase(eid); - leaveCS(s_UDTUnited.m_EPoll.m_EPollLock); + leaveCS(uglobal().m_EPoll.m_EPollLock); } void srt::CUDT::ConnectSignal(ETransmissionEvent evt, EventSlot sl) @@ -11150,7 +11177,7 @@ void srt::CUDT::EmitSignal(ETransmissionEvent tev, EventVariant var) int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) { - CUDTSocket *s = s_UDTUnited.locateSocket(u); + CUDTSocket *s = uglobal().locateSocket(u); if (!s) return -1; @@ -11173,7 +11200,7 @@ int srt::CUDT::getsndbuffer(SRTSOCKET u, size_t *blocks, size_t *bytes) int srt::CUDT::rejectReason(SRTSOCKET u) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return SRT_REJ_UNKNOWN; @@ -11182,7 +11209,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u) int srt::CUDT::rejectReason(SRTSOCKET u, int value) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); @@ -11195,7 +11222,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u, int value) int64_t srt::CUDT::socketStartTime(SRTSOCKET u) { - CUDTSocket* s = s_UDTUnited.locateSocket(u); + CUDTSocket* s = uglobal().locateSocket(u); if (!s) return APIError(MJ_NOTSUP, MN_SIDINVAL); @@ -11316,7 +11343,7 @@ void srt::CUDT::handleKeepalive(const char* /*data*/, size_t /*size*/) // existence of the group will not be changed during // the operation. The attempt of group deletion will // have to wait until this operation completes. - ScopedLock lock(s_UDTUnited.m_GlobControlLock); + ScopedLock lock(uglobal().m_GlobControlLock); CUDTGroup* pg = m_parent->m_GroupOf; if (pg) { diff --git a/srtcore/core.h b/srtcore/core.h index a9d2f9dc3..1fd5f6730 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -409,8 +409,8 @@ class CUDT return genRandomInt(0, CSeqNo::m_iMaxSeqNo); } - // For SRT_tsbpdLoop - static CUDTUnited* uglobal() { return &s_UDTUnited; } // needed by tsbpdLoop + static CUDTUnited& uglobal(); // UDT global management base + std::set& pollset() { return m_sPollID; } CSrtConfig m_config; @@ -697,7 +697,6 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - static CUDTUnited s_UDTUnited; // UDT global management base private: // Identification CUDTSocket* const m_parent; // Temporary, until the CUDTSocket class is merged with CUDT diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f05f49369..c27ae2cbe 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -248,7 +248,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) } CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) - : m_pGlobal(&CUDT::s_UDTUnited) + : m_Global(CUDT::uglobal()) , m_GroupID(-1) , m_PeerGroupID(-1) , m_selfManaged(true) @@ -282,8 +282,8 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) setupMutex(m_GroupLock, "Group"); setupMutex(m_RcvDataLock, "RcvData"); setupCond(m_RcvDataCond, "RcvData"); - m_RcvEID = m_pGlobal->m_EPoll.create(&m_RcvEpolld); - m_SndEID = m_pGlobal->m_EPoll.create(&m_SndEpolld); + m_RcvEID = m_Global.m_EPoll.create(&m_RcvEpolld); + m_SndEID = m_Global.m_EPoll.create(&m_SndEpolld); m_stats.init(); @@ -869,7 +869,7 @@ SRT_SOCKSTATUS CUDTGroup::getStatus() if (i->second == SRTS_NONEXIST) { // Otherwise find at least one socket, which's state isn't broken. - i->second = m_pGlobal->getStatus(i->first); + i->second = m_Global.getStatus(i->first); if (pending_state == SRTS_NONEXIST) pending_state = i->second; } @@ -920,7 +920,7 @@ void CUDTGroup::close() vector ids; { - ScopedLock glob(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock glob(CUDT::uglobal().m_GlobControlLock); ScopedLock g(m_GroupLock); // A non-managed group may only be closed if there are no @@ -941,7 +941,7 @@ void CUDTGroup::close() ids.push_back(ig->id); // Immediately cut ties to this group. // Just for a case, redispatch the socket, to stay safe. - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(ig->id); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(ig->id); if (!s) { HLOGC(smlog.Debug, log << "group/close: IPE(NF): group member @" << ig->id << " already deleted"); @@ -963,7 +963,7 @@ void CUDTGroup::close() { // Global EPOLL lock must be applied to access any socket's epoll set. // This is a set of all epoll ids subscribed to it. - ScopedLock elock (CUDT::s_UDTUnited.m_EPoll.m_EPollLock); + ScopedLock elock (CUDT::uglobal().m_EPoll.m_EPollLock); epollid = m_sPollID; // use move() in C++11 m_sPollID.clear(); } @@ -974,7 +974,7 @@ void CUDTGroup::close() HLOGC(smlog.Debug, log << "close: CLEARING subscription on E" << (*i) << " of $" << id()); try { - CUDT::s_UDTUnited.m_EPoll.update_usock(*i, id(), &no_events); + CUDT::uglobal().m_EPoll.update_usock(*i, id(), &no_events); } catch (...) { @@ -994,7 +994,7 @@ void CUDTGroup::close() { try { - CUDT::s_UDTUnited.close(*i); + CUDT::uglobal().close(*i); } catch (CUDTException&) { @@ -1026,7 +1026,7 @@ void CUDTGroup::close() // CSync::lock_signal(m_RcvDataCond, m_RcvDataLock); } -// [[using locked(m_pGlobal->m_GlobControlLock)]] +// [[using locked(m_Global->m_GlobControlLock)]] // [[using locked(m_GroupLock)]] void CUDTGroup::send_CheckValidSockets() { @@ -1035,7 +1035,7 @@ void CUDTGroup::send_CheckValidSockets() for (gli_t d = m_Group.begin(), d_next = d; d != m_Group.end(); d = d_next) { ++d_next; // it's now safe to erase d - CUDTSocket* revps = m_pGlobal->locateSocket_LOCKED(d->id); + CUDTSocket* revps = m_Global.locateSocket_LOCKED(d->id); if (revps != d->ps) { // Note: the socket might STILL EXIST, just in the trash, so @@ -1104,12 +1104,12 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) vector activeLinks; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) { - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -1117,7 +1117,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // LOCKED: GlobControlLock, GroupLock (RIGHT ORDER!) send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); // LOCKED: GroupLock (only) // Since this moment GlobControlLock may only be locked if GroupLock is unlocked first. @@ -1355,7 +1355,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // at the connecting stage. CEPoll::fmap_t sready; - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, @@ -1368,7 +1368,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) InvertedLock ug(m_GroupLock); THREAD_PAUSED(); - m_pGlobal->m_EPoll.swait( + m_Global.m_EPoll.swait( *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything happened THREAD_RESUMED(); } @@ -1391,7 +1391,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // Failed socket. Move d to wipeme. Remove from eid. wipeme.push_back(*i); int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, *i, &no_events); + m_Global.m_EPoll.update_usock(m_SndEID, *i, &no_events); } } @@ -1401,7 +1401,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // as redundant links at the connecting stage and became // writable (connected) before this function had a chance // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_CONNECT); + m_Global.m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_CONNECT); } } @@ -1446,7 +1446,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { { InvertedLock ung (m_GroupLock); - enterCS(CUDT::s_UDTUnited.m_GlobControlLock); + enterCS(CUDT::uglobal().m_GlobControlLock); HLOGC(gslog.Debug, log << "grp/sendBroadcast: Locked GlobControlLock, locking back GroupLock"); } @@ -1454,7 +1454,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // the Sendstate::it field shall not be used here! for (vector::iterator is = sendstates.begin(); is != sendstates.end(); ++is) { - CUDTSocket* ps = CUDT::s_UDTUnited.locateSocket_LOCKED(is->id); + CUDTSocket* ps = CUDT::uglobal().locateSocket_LOCKED(is->id); // Is the socket valid? If not, simply SKIP IT. Nothing to be done with it, // it's already deleted. @@ -1498,7 +1498,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Now you can leave GlobControlLock, while GroupLock is still locked. - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); } // Re-check after the waiting lock has been reacquired @@ -1535,7 +1535,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (was_blocked) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); if (!m_bSynSending) { throw CUDTException(MJ_AGAIN, MN_WRAVAIL, 0); @@ -1554,7 +1554,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) { HLOGC(gslog.Debug, log << "Will block on blocked socket @" << (*b)->id << " as only blocked socket remained"); - CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_SndEID, (*b)->ps, &modes); + CUDT::uglobal().epoll_add_usock_INTERNAL(m_SndEID, (*b)->ps, &modes); } const int blocklen = blocked.size(); @@ -1569,7 +1569,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) // m_iSndTimeOut is -1 by default, which matches the meaning of waiting forever THREAD_PAUSED(); - blst = m_pGlobal->m_EPoll.swait(*m_SndEpolld, sready, m_iSndTimeOut); + blst = m_Global.m_EPoll.swait(*m_SndEpolld, sready, m_iSndTimeOut); THREAD_RESUMED(); // NOTE EXCEPTIONS: @@ -1671,8 +1671,8 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (none_succeeded) { HLOGC(gslog.Debug, log << "grp/sendBroadcast: all links broken (none succeeded to send a payload)"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // Reparse error code, if set. // It might be set, if the last operation was failed. // If any operation succeeded, this will not be executed anyway. @@ -1725,7 +1725,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) if (!ready_again) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); } return rstat; @@ -1871,7 +1871,7 @@ void CUDTGroup::fillGroupData(SRT_MSGCTRL& w_out, // MSGCTRL to be written w_out.grpdata = grpdata; } -// [[using locked(CUDT::s_UDTUnited.m_GlobControLock)]] +// [[using locked(CUDT::uglobal()->m_GlobControLock)]] // [[using locked(m_GroupLock)]] struct FLookupSocketWithEvent_LOCKED { @@ -1978,7 +1978,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // which requires lock on m_GlobControlLock, while this lock cannot be applied without // first unlocking m_GroupLock. const int read_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR; - CUDT::s_UDTUnited.epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); + CUDT::uglobal().epoll_add_usock_INTERNAL(m_RcvEID, *i, &read_modes); } // Here we need to make an additional check. @@ -2014,11 +2014,11 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // This call may wait indefinite time, so GroupLock must be unlocked. InvertedLock ung (m_GroupLock); THREAD_PAUSED(); - nready = m_pGlobal->m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); + nready = m_Global.m_EPoll.swait(*m_RcvEpolld, sready, timeout, false /*report by retval*/); THREAD_RESUMED(); // HERE GlobControlLock is locked first, then GroupLock is applied back - enterCS(CUDT::s_UDTUnited.m_GlobControlLock); + enterCS(CUDT::uglobal().m_GlobControlLock); } // BOTH m_GlobControlLock AND m_GroupLock are locked here. @@ -2028,7 +2028,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& { // GlobControlLock is applied manually, so unlock manually. // GroupLock will be unlocked as per scope. - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); // This can only happen when 0 is passed as timeout and none is ready. // And 0 is passed only in non-blocking mode. So this is none ready in // non-blocking mode. @@ -2049,7 +2049,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& /*FROM*/ sready.begin(), sready.end(), /*TO*/ std::inserter(w_broken, w_broken.begin()), - /*VIA*/ FLookupSocketWithEvent_LOCKED(m_pGlobal, SRT_EPOLL_ERR)); + /*VIA*/ FLookupSocketWithEvent_LOCKED(&m_Global, SRT_EPOLL_ERR)); // If this set is empty, it won't roll even once, therefore output @@ -2081,7 +2081,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& } } - leaveCS(CUDT::s_UDTUnited.m_GlobControlLock); + leaveCS(CUDT::uglobal().m_GlobControlLock); return readReady; } @@ -2132,7 +2132,7 @@ void CUDTGroup::updateReadState(SRTSOCKET /* not sure if needed */, int32_t sequ if (ready) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); } } @@ -2145,7 +2145,7 @@ int32_t CUDTGroup::getRcvBaseSeqNo() void CUDTGroup::updateWriteState() { ScopedLock lg(m_GroupLock); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); } /// Validate iPktSeqno is in range @@ -2199,7 +2199,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) size_t output_size = 0; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) @@ -2209,13 +2209,13 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // must fist wait for being able to acquire this lock. // The group will not be deleted now because it is added usage counter // by this call, but will be released once it exits. - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } // Now, still under lock, check if all sockets still can be dispatched send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); if (m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -2254,7 +2254,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // We predict to have only one packet ahead, others are pending to be reported by tsbpd. // This will be "re-enabled" if the later check puts any new packet into ahead. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); return len; } @@ -2543,7 +2543,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) InvertedLock ung (m_GroupLock); for (set::iterator b = broken.begin(); b != broken.end(); ++b) { - CUDT::s_UDTUnited.close(*b); + CUDT::uglobal().close(*b); } } @@ -2551,7 +2551,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // All broken HLOGC(grlog.Debug, log << "group/recv: All sockets broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -2586,7 +2586,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // Don't clear the read-readinsess state if you have a packet ahead because // if you have, the next read call will return it. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); } HLOGC(grlog.Debug, @@ -2697,7 +2697,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { // Don't clear the read-readinsess state if you have a packet ahead because // if you have, the next read call will return it. - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); } return len; } @@ -3452,7 +3452,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c // at the connecting stage. CEPoll::fmap_t sready; - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // Sanity check - weird pending reported. LOGC(gslog.Error, log << "grp/send*: IPE: reported pending sockets, but EID is empty - wiping pending!"); @@ -3461,7 +3461,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c { InvertedLock ug(m_GroupLock); - m_pGlobal->m_EPoll.swait( + m_Global.m_EPoll.swait( *m_SndEpolld, sready, 0, false /*report by retval*/); // Just check if anything has happened } @@ -3472,7 +3472,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c } // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); HLOGC(gslog.Debug, log << "grp/send*: RDY: " << DisplayEpollResults(sready)); @@ -3493,7 +3493,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c sendBackup_AssignBackupState(member->pSocketData->ps->core(), BKUPST_BROKEN, currtime); const int no_events = 0; - m_pGlobal->m_EPoll.update_usock(m_SndEID, sockid, &no_events); + m_Global.m_EPoll.update_usock(m_SndEID, sockid, &no_events); } // After that, all sockets that have been reported @@ -3502,7 +3502,7 @@ void CUDTGroup::sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, c // as redundant links at the connecting stage and became // writable (connected) before this function had a chance // to check them. - m_pGlobal->m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); + m_Global.m_EPoll.clear_ready_usocks(*m_SndEpolld, SRT_EPOLL_OUT); } // [[using locked(this->m_GroupLock)]] @@ -3560,11 +3560,11 @@ void CUDTGroup::send_CloseBrokenSockets(vector& w_wipeme) // With unlocked GroupLock, we can now lock GlobControlLock. // This is needed prevent any of them be deleted from the container // at the same time. - ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock globlock(CUDT::uglobal().m_GlobControlLock); for (vector::iterator p = w_wipeme.begin(); p != w_wipeme.end(); ++p) { - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(*p); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(*p); // If the socket has been just moved to ClosedSockets, it means that // the object still exists, but it will be no longer findable. @@ -3597,7 +3597,7 @@ void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) // With unlocked GroupLock, we can now lock GlobControlLock. // This is needed prevent any of them be deleted from the container // at the same time. - ScopedLock globlock(CUDT::s_UDTUnited.m_GlobControlLock); + ScopedLock globlock(CUDT::uglobal().m_GlobControlLock); typedef vector::const_iterator const_iter_t; for (const_iter_t member = w_sendBackupCtx.memberStates().begin(); member != w_sendBackupCtx.memberStates().end(); ++member) @@ -3607,7 +3607,7 @@ void CUDTGroup::sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx) // m_GroupLock is unlocked, therefore member->pSocketData can't be used. const SRTSOCKET sockid = member->socketID; - CUDTSocket* s = CUDT::s_UDTUnited.locateSocket_LOCKED(sockid); + CUDTSocket* s = CUDT::uglobal().locateSocket_LOCKED(sockid); // If the socket has been just moved to ClosedSockets, it means that // the object still exists, but it will be no longer findable. @@ -3674,10 +3674,10 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx // Note: GroupLock is set already, skip locks and checks getGroupData_LOCKED((w_mc.grpdata), (&w_mc.grpdata_size)); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { // wipeme wiped, pending sockets checked, it can only mean that // all sockets are broken. @@ -3713,7 +3713,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx RetryWaitBlocked: { // Some sockets could have been closed in the meantime. - if (m_pGlobal->m_EPoll.empty(*m_SndEpolld)) + if (m_Global.m_EPoll.empty(*m_SndEpolld)) { HLOGC(gslog.Debug, log << "grp/sendBackup: no more sockets available for sending - group broken"); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -3723,7 +3723,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx HLOGC(gslog.Debug, log << "grp/sendBackup: swait call to get at least one link alive up to " << m_iSndTimeOut << "us"); THREAD_PAUSED(); - brdy = m_pGlobal->m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); + brdy = m_Global.m_EPoll.swait(*m_SndEpolld, (sready), m_iSndTimeOut); THREAD_RESUMED(); if (brdy == 0) // SND timeout exceeded @@ -3741,13 +3741,13 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx if (i->second & SRT_EPOLL_ERR) { SRTSOCKET id = i->first; - CUDTSocket* s = m_pGlobal->locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! + CUDTSocket* s = m_Global.locateSocket(id, CUDTUnited::ERH_RETURN); // << LOCKS m_GlobControlLock! if (s) { HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " while waiting for any writable socket - CLOSING"); - CUDT::s_UDTUnited.close(s); // << LOCKS m_GlobControlLock, then GroupLock! + CUDT::uglobal().close(s); // << LOCKS m_GlobControlLock, then GroupLock! } else { @@ -3771,8 +3771,8 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx LOGC(gslog.Error, log << "grp/sendBackup: swait=>" << brdy << " nlinks=" << nlinks << " ndead=" << ndead << " - looxlike all links broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // You can safely throw here - nothing to fill in when all sockets down. // (timeout was reported by exception in the swait call). throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -3939,18 +3939,18 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) // [[using assert(this->m_pSndBuffer != nullptr)]]; // First, acquire GlobControlLock to make sure all member sockets still exist - enterCS(m_pGlobal->m_GlobControlLock); + enterCS(m_Global.m_GlobControlLock); ScopedLock guard(m_GroupLock); if (m_bClosing) { - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } // Now, still under lock, check if all sockets still can be dispatched send_CheckValidSockets(); - leaveCS(m_pGlobal->m_GlobControlLock); + leaveCS(m_Global.m_GlobControlLock); steady_clock::time_point currtime = steady_clock::now(); @@ -4011,8 +4011,8 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (none_succeeded) { HLOGC(gslog.Debug, log << "grp/sendBackup: all links broken (none succeeded to send a payload)"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); // Reparse error code, if set. // It might be set, if the last operation was failed. // If any operation succeeded, this will not be executed anyway. @@ -4062,7 +4062,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) if (!ready_again) { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); } HLOGC(gslog.Debug, @@ -4416,7 +4416,7 @@ void CUDTGroup::setGroupConnected() if (!m_bConnected) { // Switch to connected state and give appropriate signal - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); m_bConnected = true; } } @@ -4493,19 +4493,19 @@ void CUDTGroup::activateUpdateEvent(bool still_have_items) // was deleted from the group. This might make the group empty. if (!still_have_items) // empty, or removal of unknown socket attempted - set error on group { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); } else { - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_UPDATE, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_UPDATE, true); } } void CUDTGroup::addEPoll(int eid) { - enterCS(m_pGlobal->m_EPoll.m_EPollLock); + enterCS(m_Global.m_EPoll.m_EPollLock); m_sPollID.insert(eid); - leaveCS(m_pGlobal->m_EPoll.m_EPollLock); + leaveCS(m_Global.m_EPoll.m_EPollLock); bool any_read = false; bool any_write = false; @@ -4543,14 +4543,14 @@ void CUDTGroup::addEPoll(int eid) // because we know it is, as we just added it. But it's not performance // critical, sockets are not being often added during transmission. if (any_read) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, true); if (any_write) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, true); // Set broken if none is non-broken (pending, read-ready or write-ready) if (any_broken && !any_pending) - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_ERR, true); } void CUDTGroup::removeEPollEvents(const int eid) @@ -4559,14 +4559,14 @@ void CUDTGroup::removeEPollEvents(const int eid) // since this happens after the epoll ID has been removed, they cannot be set again set remove; remove.insert(eid); - m_pGlobal->m_EPoll.update_events(id(), remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); + m_Global.m_EPoll.update_events(id(), remove, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } void CUDTGroup::removeEPollID(const int eid) { - enterCS(m_pGlobal->m_EPoll.m_EPollLock); + enterCS(m_Global.m_EPoll.m_EPollLock); m_sPollID.erase(eid); - leaveCS(m_pGlobal->m_EPoll.m_EPollLock); + leaveCS(m_Global.m_EPoll.m_EPollLock); } void CUDTGroup::updateFailedLink() @@ -4588,7 +4588,7 @@ void CUDTGroup::updateFailedLink() { // No healthy links, set ERR on epoll. HLOGC(gmlog.Debug, log << "group/updateFailedLink: All sockets broken"); - m_pGlobal->m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); } else { @@ -4597,7 +4597,7 @@ void CUDTGroup::updateFailedLink() } #if ENABLE_HEAVY_LOGGING -// [[using maybe_locked(CUDT::s_UDTUnited.m_GlobControlLock)]] +// [[using maybe_locked(CUDT::uglobal()->m_GlobControlLock)]] void CUDTGroup::debugGroup() { ScopedLock gg(m_GroupLock); diff --git a/srtcore/group.h b/srtcore/group.h index 04c8e7e01..9927a2f6d 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -395,7 +395,7 @@ class CUDTGroup // If so, grab the status of all member sockets. void getGroupCount(size_t& w_size, bool& w_still_alive); - class srt::CUDTUnited* m_pGlobal; + srt::CUDTUnited& m_Global; srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; @@ -655,7 +655,7 @@ class CUDTGroup void recv_CollectAliveAndBroken(std::vector& w_alive, std::set& w_broken); /// The function polls alive member sockets and retrieves a list of read-ready. - /// [acquires lock for CUDT::s_UDTUnited.m_GlobControlLock] + /// [acquires lock for CUDT::uglobal()->m_GlobControlLock] /// [[using locked(m_GroupLock)]] temporally unlocks-locks internally /// /// @returns list of read-ready sockets diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 03ba97e47..62d158af7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1004,7 +1004,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // be normally closed by the application, after it is done with them. // app can call any UDT API to learn the connection_broken error - CUDT::s_UDTUnited.m_EPoll.update_events( + CUDT::uglobal().m_EPoll.update_events( i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); i->u->completeBrokenConnectionDependencies(i->errorcode); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 1cfdf8ff9..46afd8981 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -211,7 +211,7 @@ TEST(TestFEC, ConfigExchange) CUDTSocket* s1; - SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1); TestMockCUDT m1; m1.core = &s1->core(); @@ -243,7 +243,7 @@ TEST(TestFEC, ConfigExchangeFaux) CUDTSocket* s1; - SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1); + SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1); const char* fec_config_wrong [] = { "FEC,Cols:20", // D: unknown filter From 790b7831fb7ec4851111e38ce601e4a13548847c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 16:18:26 +0200 Subject: [PATCH 174/683] [core] Small refax of CUDTUnited::channelSettingsMatch(..) --- srtcore/api.cpp | 127 ++++++++++++++++++++++++------------------------ srtcore/api.h | 10 +++- srtcore/core.h | 7 ++- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 38b86cf8c..dd8266f5c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1816,71 +1816,69 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) { - ScopedLock cg(s->m_ControlLock); - // a socket can "connect" only if it is in the following states: - // - OPENED: assume the socket binding parameters are configured - // - INIT: configure binding parameters here - // - any other (meaning, already connected): report error - - if (s->m_Status == SRTS_INIT) - { - if (s->core().m_config.bRendezvous) - throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); - - // If bind() was done first on this socket, then the - // socket will not perform this step. This actually does the - // same thing as bind() does, just with empty address so that - // the binding parameters are autoselected. - - s->core().open(); - sockaddr_any autoselect_sa (target_addr.family()); - // This will create such a sockaddr_any that - // will return true from empty(). - updateMux(s, autoselect_sa); // <<---- updateMux - // -> C(Snd|Rcv)Queue::init - // -> pthread_create(...C(Snd|Rcv)Queue::worker...) - s->m_Status = SRTS_OPENED; - } - else - { - if (s->m_Status != SRTS_OPENED) - throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + ScopedLock cg(s->m_ControlLock); + // a socket can "connect" only if it is in the following states: + // - OPENED: assume the socket binding parameters are configured + // - INIT: configure binding parameters here + // - any other (meaning, already connected): report error - // status = SRTS_OPENED, so family should be known already. - if (target_addr.family() != s->m_SelfAddr.family()) - { - LOGP(cnlog.Error, "srt_connect: socket is bound to a different family than target address"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } + if (s->m_Status == SRTS_INIT) + { + if (s->core().m_config.bRendezvous) + throw CUDTException(MJ_NOTSUP, MN_ISRENDUNBOUND, 0); + + // If bind() was done first on this socket, then the + // socket will not perform this step. This actually does the + // same thing as bind() does, just with empty address so that + // the binding parameters are autoselected. + + s->core().open(); + sockaddr_any autoselect_sa (target_addr.family()); + // This will create such a sockaddr_any that + // will return true from empty(). + updateMux(s, autoselect_sa); // <<---- updateMux + // -> C(Snd|Rcv)Queue::init + // -> pthread_create(...C(Snd|Rcv)Queue::worker...) + s->m_Status = SRTS_OPENED; + } + else + { + if (s->m_Status != SRTS_OPENED) + throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + // status = SRTS_OPENED, so family should be known already. + if (target_addr.family() != s->m_SelfAddr.family()) + { + LOGP(cnlog.Error, "srt_connect: socket is bound to a different family than target address"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } - // connect_complete() may be called before connect() returns. - // So we need to update the status before connect() is called, - // otherwise the status may be overwritten with wrong value - // (CONNECTED vs. CONNECTING). - s->m_Status = SRTS_CONNECTING; - /* - * In blocking mode, connect can block for up to 30 seconds for - * rendez-vous mode. Holding the s->m_ControlLock prevent close - * from cancelling the connect - */ - try - { - // record peer address - s->m_PeerAddr = target_addr; - s->core().startConnect(target_addr, forced_isn); - } - catch (CUDTException& e) // Interceptor, just to change the state. - { - s->m_Status = SRTS_OPENED; - throw e; - } + // connect_complete() may be called before connect() returns. + // So we need to update the status before connect() is called, + // otherwise the status may be overwritten with wrong value + // (CONNECTED vs. CONNECTING). + s->m_Status = SRTS_CONNECTING; - // ScopedLock destructor will delete cg and unlock s->m_ControlLock + /* + * In blocking mode, connect can block for up to 30 seconds for + * rendez-vous mode. Holding the s->m_ControlLock prevent close + * from cancelling the connect + */ + try + { + // record peer address + s->m_PeerAddr = target_addr; + s->core().startConnect(target_addr, forced_isn); + } + catch (CUDTException& e) // Interceptor, just to change the state. + { + s->m_Status = SRTS_OPENED; + throw e; + } - return 0; + return 0; } @@ -2857,9 +2855,9 @@ uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) return sa.hport(); } -bool srt::CUDTUnited::channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s) +bool srt::CUDTUnited::channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket) { - return m.m_mcfg.bReuseAddr && m.m_mcfg == s->core().m_config; + return cfgMuxer.bReuseAddr && cfgMuxer == cfgSocket; } void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* udpsock /*[[nullable]]*/) @@ -2876,6 +2874,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // If not, we need to see if there exist already a multiplexer bound // to the same endpoint. const int port = addr.hport(); + const CSrtConfig& cfgSocket = s->core().m_config; bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); @@ -2912,14 +2911,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U // Still, for ANY you need either the same family, or open // for families. - if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != s->core().m_config.iIpV6Only) + if (m.m_mcfg.iIpV6Only != -1 && m.m_mcfg.iIpV6Only != cfgSocket.iIpV6Only) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with existing IPv6 wildcard binding: " << sa.str()); throw CUDTException(MJ_NOTSUP, MN_BUSYPORT, 0); } - if ((m.m_mcfg.iIpV6Only == 0 || s->core().m_config.iIpV6Only == 0) && m.m_iIPversion != addr.family()) + if ((m.m_mcfg.iIpV6Only == 0 || cfgSocket.iIpV6Only == 0) && m.m_iIPversion != addr.family()) { LOGC(smlog.Error, log << "bind: Address: " << addr.str() << " conflicts with IPv6 wildcard binding: " << sa.str() @@ -2955,7 +2954,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& addr, const U if (reuse_attempt) { // - if the channel settings match, it can be reused - if (channelSettingsMatch(m, s)) + if (channelSettingsMatch(m.m_mcfg, cfgSocket)) { HLOGC(smlog.Debug, log << "bind: reusing multiplexer for port " << port); // reuse the existing multiplexer diff --git a/srtcore/api.h b/srtcore/api.h index a1bfbffd7..516322292 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,7 +123,8 @@ class CUDTSocket void construct(); - srt::sync::atomic m_Status; //< current socket state + SRT_ATTR_GUARDED_BY(m_ControlLock) + sync::atomic m_Status; //< current socket state /// Time when the socket is closed. /// When the socket is closed, it is not removed immediately from the list @@ -441,7 +442,12 @@ friend class CRendezvousQueue; // Utility functions for updateMux void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm); - bool channelSettingsMatch(const CMultiplexer& m, const CUDTSocket* s); + + /// @brief Checks if channel configuration matches the socket configuration. + /// @param cfgMuxer multiplexer configuration. + /// @param cfgSocket socket configuration. + /// @return tru if configurations match, false otherwise. + static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket); private: std::map m_mMultiplexer; // UDP multiplexer diff --git a/srtcore/core.h b/srtcore/core.h index 1fd5f6730..e74c58c6e 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -499,14 +499,17 @@ class CUDT SRT_ATR_NODISCARD size_t fillSrtHandshake_HSRSP(uint32_t* srtdata, size_t srtlen, int hs_version); SRT_ATR_NODISCARD size_t fillSrtHandshake(uint32_t* srtdata, size_t srtlen, int msgtype, int hs_version); - SRT_ATR_NODISCARD bool createSrtHandshake(int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen, + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + bool createSrtHandshake(int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen, CPacket& w_reqpkt, CHandShake& w_hs); SRT_ATR_NODISCARD size_t fillHsExtConfigString(uint32_t *pcmdspec, int cmd, const std::string &str); #if ENABLE_EXPERIMENTAL_BONDING SRT_ATR_NODISCARD size_t fillHsExtGroup(uint32_t *pcmdspec); #endif - SRT_ATR_NODISCARD size_t fillHsExtKMREQ(uint32_t *pcmdspec, size_t ki); + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) + size_t fillHsExtKMREQ(uint32_t *pcmdspec, size_t ki); + SRT_ATR_NODISCARD size_t fillHsExtKMRSP(uint32_t *pcmdspec, const uint32_t *kmdata, size_t kmdata_wordsize); SRT_ATR_NODISCARD size_t prepareSrtHsMsg(int cmd, uint32_t* srtdata, size_t size); From 2031b2c696f5f1bbffddf54b24c747c0db7d0a63 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 16 Aug 2021 16:20:16 +0200 Subject: [PATCH 175/683] [core] Do not set peerAddress in connectIn(..). CUDT::startConnect(..) sets it itself. --- srtcore/api.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index dd8266f5c..e35d637a5 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1868,8 +1868,6 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i */ try { - // record peer address - s->m_PeerAddr = target_addr; s->core().startConnect(target_addr, forced_isn); } catch (CUDTException& e) // Interceptor, just to change the state. From ce2742b9ce6546a5252367b23c63404bfbfeb7f1 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 5 Oct 2021 10:55:47 +0200 Subject: [PATCH 176/683] [apps] Split off stats writer from apputil.cpp (#2130) --- apps/apputil.cpp | 334 +---------------------------------- apps/apputil.hpp | 87 ---------- apps/statswriter.cpp | 355 ++++++++++++++++++++++++++++++++++++++ apps/statswriter.hpp | 107 ++++++++++++ apps/support.maf | 1 + apps/transmitbase.hpp | 1 + testing/srt-test-live.maf | 1 + testing/testmedia.hpp | 1 + 8 files changed, 467 insertions(+), 420 deletions(-) create mode 100644 apps/statswriter.cpp create mode 100644 apps/statswriter.hpp diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 1389b748e..68a9c4670 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -16,6 +16,7 @@ #include #include +#include "srt.h" // Required for SRT_SYNC_CLOCK_* definitions. #include "apputil.hpp" #include "netinet_any.h" #include "srt_compat.h" @@ -352,339 +353,6 @@ string OptionHelpItem(const OptionName& o) return out; } -// Stats module - -// Note: std::put_time is supported only in GCC 5 and higher -#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) -#define HAS_PUT_TIME -#endif - -template -inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, - TYPE CBytePerfMon::*field) -{ - return new SrtStatDataType(cat, name, longname, field); -} - -#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field)) -#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field) - -vector> g_SrtStatsTable; - -struct SrtStatsTableInit -{ - SrtStatsTableInit(vector>& s) - { - STATX(GEN, time, Time, msTimeStamp); - - STAT(WINDOW, flow, pktFlowWindow); - STAT(WINDOW, congestion, pktCongestionWindow); - STAT(WINDOW, flight, pktFlightSize); - - STAT(LINK, rtt, msRTT); - STAT(LINK, bandwidth, mbpsBandwidth); - STAT(LINK, maxBandwidth, mbpsMaxBW); - - STAT(SEND, packets, pktSent); - STAT(SEND, packetsUnique, pktSentUnique); - STAT(SEND, packetsLost, pktSndLoss); - STAT(SEND, packetsDropped, pktSndDrop); - STAT(SEND, packetsRetransmitted, pktRetrans); - STAT(SEND, packetsFilterExtra, pktSndFilterExtra); - STAT(SEND, bytes, byteSent); - STAT(SEND, bytesUnique, byteSentUnique); - STAT(SEND, bytesDropped, byteSndDrop); - STAT(SEND, byteAvailBuf, byteAvailSndBuf); - STAT(SEND, msBuf, msSndBuf); - STAT(SEND, mbitRate, mbpsSendRate); - STAT(SEND, sendPeriod, usPktSndPeriod); - - STAT(RECV, packets, pktRecv); - STAT(RECV, packetsUnique, pktRecvUnique); - STAT(RECV, packetsLost, pktRcvLoss); - STAT(RECV, packetsDropped, pktRcvDrop); - STAT(RECV, packetsRetransmitted, pktRcvRetrans); - STAT(RECV, packetsBelated, pktRcvBelated); - STAT(RECV, packetsFilterExtra, pktRcvFilterExtra); - STAT(RECV, packetsFilterSupply, pktRcvFilterSupply); - STAT(RECV, packetsFilterLoss, pktRcvFilterLoss); - STAT(RECV, bytes, byteRecv); - STAT(RECV, bytesUnique, byteRecvUnique); - STAT(RECV, bytesLost, byteRcvLoss); - STAT(RECV, bytesDropped, byteRcvDrop); - STAT(RECV, byteAvailBuf, byteAvailRcvBuf); - STAT(RECV, msBuf, msRcvBuf); - STAT(RECV, mbitRate, mbpsRecvRate); - STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); - } -} g_SrtStatsTableInit (g_SrtStatsTable); - - -#undef STAT -#undef STATX - -string srt_json_cat_names [] = { - "", - "window", - "link", - "send", - "recv" -}; - -#ifdef HAS_PUT_TIME -// Follows ISO 8601 -std::string SrtStatsWriter::print_timestamp() -{ - using namespace std; - using namespace std::chrono; - - const auto systime_now = system_clock::now(); - const time_t time_now = system_clock::to_time_t(systime_now); - - std::ostringstream output; - - // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. - const tm tm_now = SysLocalTime(time_now); - output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); - const auto since_epoch = systime_now.time_since_epoch(); - const seconds s = duration_cast(since_epoch); - output << duration_cast(since_epoch - s).count(); - output << std::put_time(&tm_now, "%z"); - return output.str(); -} -#else - -// This is a stub. The error when not defining it would be too -// misleading, so this stub will work if someone mistakenly adds -// the item to the output format without checking that HAS_PUT_TIME. -string SrtStatsWriter::print_timestamp() -{ return ""; } -#endif // HAS_PUT_TIME - - -class SrtStatsJson : public SrtStatsWriter -{ - static string quotekey(const string& name) - { - if (name == "") - return ""; - - return R"(")" + name + R"(":)"; - } - - static string quote(const string& name) - { - if (name == "") - return ""; - - return R"(")" + name + R"(")"; - } - -public: - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - - string pretty_cr, pretty_tab; - if (Option("pretty")) - { - pretty_cr = "\n"; - pretty_tab = "\t"; - } - - SrtStatCat cat = SSC_GEN; - - // Do general manually - output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr; - - // SID is displayed manually - output << pretty_tab << quotekey("sid") << sid; - - // Extra Timepoint is also displayed manually -#ifdef HAS_PUT_TIME - // NOTE: still assumed SSC_GEN category - output << "," << pretty_cr << pretty_tab - << quotekey("timepoint") << quote(print_timestamp()); -#endif - - // Now continue with fields as specified in the table - for (auto& i: g_SrtStatsTable) - { - if (i->category == cat) - { - output << ","; // next item in same cat - output << pretty_cr; - output << pretty_tab; - if (cat != SSC_GEN) - output << pretty_tab; - } - else - { - if (cat != SSC_GEN) - { - // DO NOT close if general category, just - // enter the depth. - output << pretty_cr << pretty_tab << "}"; - } - cat = i->category; - output << ","; - output << pretty_cr; - if (cat != SSC_GEN) - output << pretty_tab; - - output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; - if (cat != SSC_GEN) - output << pretty_tab; - } - - // Print the current field - output << quotekey(i->name); - i->PrintValue(output, mon); - } - - // Close the previous subcategory - if (cat != SSC_GEN) - { - output << pretty_cr << pretty_tab << "}" << pretty_cr; - } - - // Close the general category entity - output << "}" << pretty_cr << endl; - - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl; - return output.str(); - } -}; - -class SrtStatsCsv : public SrtStatsWriter -{ -private: - bool first_line_printed; - -public: - SrtStatsCsv() : first_line_printed(false) {} - - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - - // Header - if (!first_line_printed) - { -#ifdef HAS_PUT_TIME - output << "Timepoint,"; -#endif - output << "Time,SocketID"; - - for (auto& i: g_SrtStatsTable) - { - output << "," << i->longname; - } - output << endl; - first_line_printed = true; - } - - // Values -#ifdef HAS_PUT_TIME - // HDR: Timepoint - output << print_timestamp() << ","; -#endif // HAS_PUT_TIME - - // HDR: Time,SocketID - output << mon.msTimeStamp << "," << sid; - - // HDR: the loop of all values in g_SrtStatsTable - for (auto& i: g_SrtStatsTable) - { - output << ","; - i->PrintValue(output, mon); - } - - output << endl; - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; - return output.str(); - } -}; - -class SrtStatsCols : public SrtStatsWriter -{ -public: - string WriteStats(int sid, const CBytePerfMon& mon) override - { - std::ostringstream output; - output << "======= SRT STATS: sid=" << sid << endl; - output << "PACKETS SENT: " << setw(11) << mon.pktSent << " RECEIVED: " << setw(11) << mon.pktRecv << endl; - output << "LOST PKT SENT: " << setw(11) << mon.pktSndLoss << " RECEIVED: " << setw(11) << mon.pktRcvLoss << endl; - output << "REXMIT SENT: " << setw(11) << mon.pktRetrans << " RECEIVED: " << setw(11) << mon.pktRcvRetrans << endl; - output << "DROP PKT SENT: " << setw(11) << mon.pktSndDrop << " RECEIVED: " << setw(11) << mon.pktRcvDrop << endl; - output << "FILTER EXTRA TX: " << setw(11) << mon.pktSndFilterExtra << " RX: " << setw(11) << mon.pktRcvFilterExtra << endl; - output << "FILTER RX SUPPL: " << setw(11) << mon.pktRcvFilterSupply << " RX LOSS: " << setw(11) << mon.pktRcvFilterLoss << endl; - output << "RATE SENDING: " << setw(11) << mon.mbpsSendRate << " RECEIVING: " << setw(11) << mon.mbpsRecvRate << endl; - output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl; - output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl; - output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl; - output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; - output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl; - return output.str(); - } - - string WriteBandwidth(double mbpsBandwidth) override - { - std::ostringstream output; - output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; - return output.str(); - } -}; - -shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat) -{ - switch (printformat) - { - case SRTSTATS_PROFMAT_JSON: - return make_shared(); - case SRTSTATS_PROFMAT_CSV: - return make_shared(); - case SRTSTATS_PROFMAT_2COLS: - return make_shared(); - default: - break; - } - return nullptr; -} - -SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) -{ - size_t havecomma = pf.find(','); - if (havecomma != string::npos) - { - w_extras = pf.substr(havecomma+1); - pf = pf.substr(0, havecomma); - } - - if (pf == "default") - return SRTSTATS_PROFMAT_2COLS; - - if (pf == "json") - return SRTSTATS_PROFMAT_JSON; - - if (pf == "csv") - return SRTSTATS_PROFMAT_CSV; - - return SRTSTATS_PROFMAT_INVALID; -} - const char* SRTClockTypeStr() { const int clock_type = srt_clock_type(); diff --git a/apps/apputil.hpp b/apps/apputil.hpp index 782412815..7737c05f7 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -77,8 +77,6 @@ inline void SysCleanupNetwork() {} #endif -#include "srt.h" // Required for stats module - #ifdef _WIN32 inline int SysError() { return ::GetLastError(); } const int SysAGAIN = WSAEWOULDBLOCK; @@ -335,92 +333,7 @@ inline bool OptionPresent(const options_t& options, const std::set& options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); std::string OptionHelpItem(const OptionName& o); -// Statistics module - -enum SrtStatsPrintFormat -{ - SRTSTATS_PROFMAT_INVALID = -1, - SRTSTATS_PROFMAT_2COLS = 0, - SRTSTATS_PROFMAT_JSON, - SRTSTATS_PROFMAT_CSV -}; - -SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras); - -enum SrtStatCat -{ - SSC_GEN, //< General - SSC_WINDOW, // flow/congestion window - SSC_LINK, //< Link data - SSC_SEND, //< Sending - SSC_RECV //< Receiving -}; - -struct SrtStatData -{ - SrtStatCat category; - std::string name; - std::string longname; - - SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} - virtual ~SrtStatData() {} - - virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; -}; - -template -struct SrtStatDataType: public SrtStatData -{ - typedef TYPE CBytePerfMon::*pfield_t; - pfield_t pfield; - - SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field) - : SrtStatData (cat, name, longname), pfield(field) - { - } - - void PrintValue(std::ostream& str, const CBytePerfMon& mon) override - { - str << mon.*pfield; - } -}; - -class SrtStatsWriter -{ -public: - virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; - virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; - virtual ~SrtStatsWriter() { }; - - // Only if HAS_PUT_TIME. Specified in the imp file. - std::string print_timestamp(); - - void Option(const std::string& key, const std::string& val) - { - options[key] = val; - } - - bool Option(const std::string& key, std::string* rval = nullptr) - { - const std::string* out = map_getp(options, key); - if (!out) - return false; - - if (rval) - *rval = *out; - return true; - } - -protected: - std::map options; -}; - -extern std::vector> g_SrtStatsTable; - -std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); - const char* SRTClockTypeStr(); void PrintLibVersion(); - #endif // INC_SRT_APPCOMMON_H diff --git a/apps/statswriter.cpp b/apps/statswriter.cpp new file mode 100644 index 000000000..c176248ab --- /dev/null +++ b/apps/statswriter.cpp @@ -0,0 +1,355 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "statswriter.hpp" +#include "netinet_any.h" +#include "srt_compat.h" + +// Note: std::put_time is supported only in GCC 5 and higher +#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5) +#define HAS_PUT_TIME +#endif + +using namespace std; + + +template +inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname, + TYPE CBytePerfMon::*field) +{ + return new SrtStatDataType(cat, name, longname, field); +} + +#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field)) +#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field) + +vector> g_SrtStatsTable; + +struct SrtStatsTableInit +{ + SrtStatsTableInit(vector>& s) + { + STATX(GEN, time, Time, msTimeStamp); + + STAT(WINDOW, flow, pktFlowWindow); + STAT(WINDOW, congestion, pktCongestionWindow); + STAT(WINDOW, flight, pktFlightSize); + + STAT(LINK, rtt, msRTT); + STAT(LINK, bandwidth, mbpsBandwidth); + STAT(LINK, maxBandwidth, mbpsMaxBW); + + STAT(SEND, packets, pktSent); + STAT(SEND, packetsUnique, pktSentUnique); + STAT(SEND, packetsLost, pktSndLoss); + STAT(SEND, packetsDropped, pktSndDrop); + STAT(SEND, packetsRetransmitted, pktRetrans); + STAT(SEND, packetsFilterExtra, pktSndFilterExtra); + STAT(SEND, bytes, byteSent); + STAT(SEND, bytesUnique, byteSentUnique); + STAT(SEND, bytesDropped, byteSndDrop); + STAT(SEND, byteAvailBuf, byteAvailSndBuf); + STAT(SEND, msBuf, msSndBuf); + STAT(SEND, mbitRate, mbpsSendRate); + STAT(SEND, sendPeriod, usPktSndPeriod); + + STAT(RECV, packets, pktRecv); + STAT(RECV, packetsUnique, pktRecvUnique); + STAT(RECV, packetsLost, pktRcvLoss); + STAT(RECV, packetsDropped, pktRcvDrop); + STAT(RECV, packetsRetransmitted, pktRcvRetrans); + STAT(RECV, packetsBelated, pktRcvBelated); + STAT(RECV, packetsFilterExtra, pktRcvFilterExtra); + STAT(RECV, packetsFilterSupply, pktRcvFilterSupply); + STAT(RECV, packetsFilterLoss, pktRcvFilterLoss); + STAT(RECV, bytes, byteRecv); + STAT(RECV, bytesUnique, byteRecvUnique); + STAT(RECV, bytesLost, byteRcvLoss); + STAT(RECV, bytesDropped, byteRcvDrop); + STAT(RECV, byteAvailBuf, byteAvailRcvBuf); + STAT(RECV, msBuf, msRcvBuf); + STAT(RECV, mbitRate, mbpsRecvRate); + STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay); + } +} g_SrtStatsTableInit (g_SrtStatsTable); + + +#undef STAT +#undef STATX + +string srt_json_cat_names [] = { + "", + "window", + "link", + "send", + "recv" +}; + +#ifdef HAS_PUT_TIME +// Follows ISO 8601 +std::string SrtStatsWriter::print_timestamp() +{ + using namespace std; + using namespace std::chrono; + + const auto systime_now = system_clock::now(); + const time_t time_now = system_clock::to_time_t(systime_now); + + std::ostringstream output; + + // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time. + const tm tm_now = SysLocalTime(time_now); + output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6); + const auto since_epoch = systime_now.time_since_epoch(); + const seconds s = duration_cast(since_epoch); + output << duration_cast(since_epoch - s).count(); + output << std::put_time(&tm_now, "%z"); + return output.str(); +} +#else + +// This is a stub. The error when not defining it would be too +// misleading, so this stub will work if someone mistakenly adds +// the item to the output format without checking that HAS_PUT_TIME. +string SrtStatsWriter::print_timestamp() +{ return ""; } +#endif // HAS_PUT_TIME + + +class SrtStatsJson : public SrtStatsWriter +{ + static string quotekey(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(":)"; + } + + static string quote(const string& name) + { + if (name == "") + return ""; + + return R"(")" + name + R"(")"; + } + +public: + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + + string pretty_cr, pretty_tab; + if (Option("pretty")) + { + pretty_cr = "\n"; + pretty_tab = "\t"; + } + + SrtStatCat cat = SSC_GEN; + + // Do general manually + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr; + + // SID is displayed manually + output << pretty_tab << quotekey("sid") << sid; + + // Extra Timepoint is also displayed manually +#ifdef HAS_PUT_TIME + // NOTE: still assumed SSC_GEN category + output << "," << pretty_cr << pretty_tab + << quotekey("timepoint") << quote(print_timestamp()); +#endif + + // Now continue with fields as specified in the table + for (auto& i: g_SrtStatsTable) + { + if (i->category == cat) + { + output << ","; // next item in same cat + output << pretty_cr; + output << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + else + { + if (cat != SSC_GEN) + { + // DO NOT close if general category, just + // enter the depth. + output << pretty_cr << pretty_tab << "}"; + } + cat = i->category; + output << ","; + output << pretty_cr; + if (cat != SSC_GEN) + output << pretty_tab; + + output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab; + if (cat != SSC_GEN) + output << pretty_tab; + } + + // Print the current field + output << quotekey(i->name); + i->PrintValue(output, mon); + } + + // Close the previous subcategory + if (cat != SSC_GEN) + { + output << pretty_cr << pretty_tab << "}" << pretty_cr; + } + + // Close the general category entity + output << "}" << pretty_cr << endl; + + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl; + return output.str(); + } +}; + +class SrtStatsCsv : public SrtStatsWriter +{ +private: + bool first_line_printed; + +public: + SrtStatsCsv() : first_line_printed(false) {} + + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + + // Header + if (!first_line_printed) + { +#ifdef HAS_PUT_TIME + output << "Timepoint,"; +#endif + output << "Time,SocketID"; + + for (auto& i: g_SrtStatsTable) + { + output << "," << i->longname; + } + output << endl; + first_line_printed = true; + } + + // Values +#ifdef HAS_PUT_TIME + // HDR: Timepoint + output << print_timestamp() << ","; +#endif // HAS_PUT_TIME + + // HDR: Time,SocketID + output << mon.msTimeStamp << "," << sid; + + // HDR: the loop of all values in g_SrtStatsTable + for (auto& i: g_SrtStatsTable) + { + output << ","; + i->PrintValue(output, mon); + } + + output << endl; + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; + return output.str(); + } +}; + +class SrtStatsCols : public SrtStatsWriter +{ +public: + string WriteStats(int sid, const CBytePerfMon& mon) override + { + std::ostringstream output; + output << "======= SRT STATS: sid=" << sid << endl; + output << "PACKETS SENT: " << setw(11) << mon.pktSent << " RECEIVED: " << setw(11) << mon.pktRecv << endl; + output << "LOST PKT SENT: " << setw(11) << mon.pktSndLoss << " RECEIVED: " << setw(11) << mon.pktRcvLoss << endl; + output << "REXMIT SENT: " << setw(11) << mon.pktRetrans << " RECEIVED: " << setw(11) << mon.pktRcvRetrans << endl; + output << "DROP PKT SENT: " << setw(11) << mon.pktSndDrop << " RECEIVED: " << setw(11) << mon.pktRcvDrop << endl; + output << "FILTER EXTRA TX: " << setw(11) << mon.pktSndFilterExtra << " RX: " << setw(11) << mon.pktRcvFilterExtra << endl; + output << "FILTER RX SUPPL: " << setw(11) << mon.pktRcvFilterSupply << " RX LOSS: " << setw(11) << mon.pktRcvFilterLoss << endl; + output << "RATE SENDING: " << setw(11) << mon.mbpsSendRate << " RECEIVING: " << setw(11) << mon.mbpsRecvRate << endl; + output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl; + output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl; + output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl; + output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl; + output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl; + return output.str(); + } + + string WriteBandwidth(double mbpsBandwidth) override + { + std::ostringstream output; + output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl; + return output.str(); + } +}; + +shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat) +{ + switch (printformat) + { + case SRTSTATS_PROFMAT_JSON: + return make_shared(); + case SRTSTATS_PROFMAT_CSV: + return make_shared(); + case SRTSTATS_PROFMAT_2COLS: + return make_shared(); + default: + break; + } + return nullptr; +} + +SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras) +{ + size_t havecomma = pf.find(','); + if (havecomma != string::npos) + { + w_extras = pf.substr(havecomma+1); + pf = pf.substr(0, havecomma); + } + + if (pf == "default") + return SRTSTATS_PROFMAT_2COLS; + + if (pf == "json") + return SRTSTATS_PROFMAT_JSON; + + if (pf == "csv") + return SRTSTATS_PROFMAT_CSV; + + return SRTSTATS_PROFMAT_INVALID; +} diff --git a/apps/statswriter.hpp b/apps/statswriter.hpp new file mode 100644 index 000000000..e15b902b7 --- /dev/null +++ b/apps/statswriter.hpp @@ -0,0 +1,107 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + + +#ifndef INC_SRT_APPS_STATSWRITER_H +#define INC_SRT_APPS_STATSWRITER_H + +#include +#include +#include +#include + +#include "srt.h" +#include "utilities.h" + +enum SrtStatsPrintFormat +{ + SRTSTATS_PROFMAT_INVALID = -1, + SRTSTATS_PROFMAT_2COLS = 0, + SRTSTATS_PROFMAT_JSON, + SRTSTATS_PROFMAT_CSV +}; + +SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras); + +enum SrtStatCat +{ + SSC_GEN, //< General + SSC_WINDOW, // flow/congestion window + SSC_LINK, //< Link data + SSC_SEND, //< Sending + SSC_RECV //< Receiving +}; + +struct SrtStatData +{ + SrtStatCat category; + std::string name; + std::string longname; + + SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {} + virtual ~SrtStatData() {} + + virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0; +}; + +template +struct SrtStatDataType: public SrtStatData +{ + typedef TYPE CBytePerfMon::*pfield_t; + pfield_t pfield; + + SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field) + : SrtStatData (cat, name, longname), pfield(field) + { + } + + void PrintValue(std::ostream& str, const CBytePerfMon& mon) override + { + str << mon.*pfield; + } +}; + +class SrtStatsWriter +{ +public: + virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0; + virtual std::string WriteBandwidth(double mbpsBandwidth) = 0; + virtual ~SrtStatsWriter() { }; + + // Only if HAS_PUT_TIME. Specified in the imp file. + std::string print_timestamp(); + + void Option(const std::string& key, const std::string& val) + { + options[key] = val; + } + + bool Option(const std::string& key, std::string* rval = nullptr) + { + const std::string* out = map_getp(options, key); + if (!out) + return false; + + if (rval) + *rval = *out; + return true; + } + +protected: + std::map options; +}; + +extern std::vector> g_SrtStatsTable; + +std::shared_ptr SrtStatsWriterFactory(SrtStatsPrintFormat printformat); + + + +#endif diff --git a/apps/support.maf b/apps/support.maf index 19ac894b4..e5aa77b8e 100644 --- a/apps/support.maf +++ b/apps/support.maf @@ -9,6 +9,7 @@ SOURCES apputil.cpp +statswriter.cpp logsupport.cpp logsupport_appdefs.cpp socketoptions.cpp diff --git a/apps/transmitbase.hpp b/apps/transmitbase.hpp index 3b3c7d0b9..007f4aaee 100644 --- a/apps/transmitbase.hpp +++ b/apps/transmitbase.hpp @@ -19,6 +19,7 @@ #include "srt.h" #include "uriparser.hpp" #include "apputil.hpp" +#include "statswriter.hpp" typedef std::vector bytevector; extern bool transmit_total_stats; diff --git a/testing/srt-test-live.maf b/testing/srt-test-live.maf index 17368a660..e80d79ec7 100644 --- a/testing/srt-test-live.maf +++ b/testing/srt-test-live.maf @@ -4,6 +4,7 @@ SOURCES srt-test-live.cpp testmedia.cpp ../apps/apputil.cpp +../apps/statswriter.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp ../apps/uriparser.cpp diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index d3447a0d0..ec378080e 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -18,6 +18,7 @@ #include #include "apputil.hpp" +#include "statswriter.hpp" #include "testmediabase.hpp" #include // Needs access to CUDTException #include From 73b49feb9031d78828ac18d412c196e2b3d05b57 Mon Sep 17 00:00:00 2001 From: Sergei Ignatov Date: Wed, 6 Oct 2021 19:44:36 +1000 Subject: [PATCH 177/683] [build] Support NDK r23, OpenSSL v3 (#2148) --- scripts/build-android/build-android | 18 ++++---- scripts/build-android/mkssl | 68 +++++------------------------ 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index 0d56a8f01..f2d596847 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -3,16 +3,16 @@ echo_help() { echo "Usage: $0 [options...]" - echo " -n Specify NDK root path for the build." - echo " -a Select target API level." - echo " -t Select target architectures." - echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64." + echo " -n NDK root path for the build" + echo " -a Target API level" + echo " -t Space-separated list of target architectures" + echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64" echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" - echo " -o Select OpenSSL (1.1.1 series) version. E.g. 1.1.1h" - echo " -m Select Mbed TLS version. E.g. v2.26.0" - echo " -s Select SRT version. E.g. v1.4.3" + echo " -o OpenSSL version. E.g. 1.1.1l" + echo " -m Mbed TLS version. E.g. v2.26.0" + echo " -s SRT version. E.g. v1.4.4" echo - echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/21.4.7075529 -a 28 -t \"armeabi-v7a arm64-v8a x86 x86_64\" -o 1.1.1h -s v1.4.3" + echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\"" echo } @@ -20,7 +20,7 @@ echo_help() NDK_ROOT="" API_LEVEL=28 BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" -OPENSSL_VERSION=1.1.1h +OPENSSL_VERSION=1.1.1l SRT_VERSION="" ENC_LIB=openssl MBEDTLS_VERSION=v2.26.0 diff --git a/scripts/build-android/mkssl b/scripts/build-android/mkssl index 4e9d0df28..ad6547261 100755 --- a/scripts/build-android/mkssl +++ b/scripts/build-android/mkssl @@ -29,48 +29,22 @@ fi cd openssl-${OPENSSL_VERSION} || exit 128 -##### Prepare Files ##### -sed -i.bak 's/.*-mandroid.*//' Configurations/15-android.conf -patch -p1 -N <{\$lib}. \$shlibvariant. '\$(SHLIB_EXT)'; -+ -+ if (windowsdll()) { -+ return \$lib . '\$(SHLIB_EXT_IMPORT)'; -+ } -+ return \$lib . '\$(SHLIB_EXT_SIMPLE)'; - } -- sub shlib_simple { -+ -+ sub shlib { - my \$lib = shift; - return () if \$disabled{shared} || \$lib =~ /\\.a$/; - -EOP - -##### remove output-directory ##### -#rm -rf $OUT_DIR - ##### export ndk directory. Required by openssl-build-scripts ##### -export ANDROID_NDK +case ${OPENSSL_VERSION} in + 1.1.1*) + export ANDROID_NDK_HOME=$ANDROID_NDK + ;; + *) + export ANDROID_NDK_ROOT=$ANDROID_NDK + ;; +esac + +export PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin:$PATH ##### build-function ##### build_the_thing() { - TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG - export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:"$PATH" -echo $PATH make clean - #./Configure $SSL_TARGET $OPTIONS -fuse-ld="$TOOLCHAIN/$TRIBLE/bin/ld" "-gcc-toolchain $TOOLCHAIN" && \ - ./Configure $SSL_TARGET $OPTIONS -fuse-ld="$TOOLCHAIN/$TRIBLE/bin/ld" && \ + ./Configure $SSL_TARGET -D__ANDROID_API__=$API_LEVEL && \ make && \ make install DESTDIR=$DESTDIR || exit 128 } @@ -79,39 +53,19 @@ echo $PATH for build_target in $BUILD_TARGETS do case $build_target in - armeabi) - TRIBLE="arm-linux-androideabi" - TC_NAME="arm-linux-androideabi-4.9" - #OPTIONS="--target=armv5te-linux-androideabi -mthumb -fPIC -latomic -D__ANDROID_API__=$API_LEVEL" - OPTIONS="--target=armv5te-linux-androideabi -mthumb -fPIC -latomic -D__ANDROID_API__=$API_LEVEL" - DESTDIR="$BUILD_DIR/armeabi" - SSL_TARGET="android-arm" - ;; armeabi-v7a) - TRIBLE="arm-linux-androideabi" - TC_NAME="arm-linux-androideabi-4.9" - OPTIONS="--target=armv7a-linux-androideabi -Wl,--fix-cortex-a8 -fPIC -D__ANDROID_API__=$API_LEVEL" DESTDIR="$BUILD_DIR/armeabi-v7a" SSL_TARGET="android-arm" ;; x86) - TRIBLE="i686-linux-android" - TC_NAME="x86-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/x86" SSL_TARGET="android-x86" ;; x86_64) - TRIBLE="x86_64-linux-android" - TC_NAME="x86_64-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/x86_64" SSL_TARGET="android-x86_64" ;; arm64-v8a) - TRIBLE="aarch64-linux-android" - TC_NAME="aarch64-linux-android-4.9" - OPTIONS="-fPIC -D__ANDROID_API__=${API_LEVEL}" DESTDIR="$BUILD_DIR/arm64-v8a" SSL_TARGET="android-arm64" ;; From 2fb3c9ab06574b042590ff044a5ac06ad278e0b6 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 11 Oct 2021 19:01:45 +0530 Subject: [PATCH 178/683] [api] Use the SOCKET type for any WIN32 environment (#2152) including MinGW. --- srtcore/srt.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/srtcore/srt.h b/srtcore/srt.h index 496112249..4cc2a377f 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -152,11 +152,7 @@ typedef int32_t SRTSOCKET; static const int32_t SRTGROUP_MASK = (1 << 30); #ifdef _WIN32 - #ifndef __MINGW32__ - typedef SOCKET SYSSOCKET; - #else - typedef int SYSSOCKET; - #endif + typedef SOCKET SYSSOCKET; #else typedef int SYSSOCKET; #endif From 6886bfaa6d23ffe74756fdf7ec3b4e7f89ae860d Mon Sep 17 00:00:00 2001 From: hondaxiao Date: Tue, 12 Oct 2021 11:15:20 +0800 Subject: [PATCH 179/683] [build] Add enable-experimental-bonding opt in configure --- configure-data.tcl | 1 + 1 file changed, 1 insertion(+) diff --git a/configure-data.tcl b/configure-data.tcl index 5c0ee2ee7..ab9dfd635 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -54,6 +54,7 @@ set cmake_options { enable-getnameinfo "In-logs sockaddr-to-string should do rev-dns (default: OFF)" enable-unittests "Enable unit tests (default: OFF)" enable-thread-check "Enable #include that implements THREAD_* macros" + enable-experimental-bonding "Enable experimental bonding (default: OFF)" openssl-crypto-library= "Path to a library." openssl-include-dir= "Path to a file." openssl-ssl-library= "Path to a library." From 86d1eb2d98491c71313e887c1bedcecce0932f90 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 5 Oct 2021 17:12:44 +0200 Subject: [PATCH 180/683] [core] Added CUDT::isRcvReady() with mutex lock. Added CUDT::getAvailRcvBufferSize() function with and without mutex lock. --- srtcore/common.h | 2 +- srtcore/core.cpp | 66 ++++++++++++++++++++++++++++++------------------ srtcore/core.h | 28 ++++++++++++++------ srtcore/sync.h | 14 +++++----- 4 files changed, 68 insertions(+), 42 deletions(-) diff --git a/srtcore/common.h b/srtcore/common.h index 530759dde..3e2ce9363 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -616,7 +616,7 @@ class CSeqNo /// This behaves like seq1 - seq2, in comparison to numbers, /// and with the statement that only the sign of the result matters. - /// That is, it returns a negative value if seq1 < seq2, + /// Returns a negative value if seq1 < seq2, /// positive if seq1 > seq2, and zero if they are equal. /// The only correct application of this function is when you /// compare two values and it works faster than seqoff. However diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 53e4a28d5..b3b1d374c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -551,7 +551,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) else { enterCS(m_RecvLock); - if (m_pRcvBuffer && m_pRcvBuffer->isRcvDataReady()) + if (m_pRcvBuffer && isRcvBufferReady()) event |= SRT_EPOLL_IN; leaveCS(m_RecvLock); if (m_pSndBuffer && (m_config.iSndBufSize > m_pSndBuffer->getCurrBufSize())) @@ -5211,7 +5211,6 @@ void * srt::CUDT::tsbpd(void *param) { int32_t skiptoseqno = SRT_SEQNO_NONE; bool passack = true; // Get next packet to wait for even if not acked - rxready = self->m_pRcvBuffer->getRcvFirstMsg((tsbpdtime), (passack), (skiptoseqno), (current_pkt_seq), rcv_base_seq); HLOGC(tslog.Debug, @@ -6052,7 +6051,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) UniqueLock recvguard(m_RecvLock); - if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (m_bShutdown) { @@ -6084,7 +6083,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) CSync rcond (m_RecvDataCond, recvguard); CSync tscond (m_RcvTsbPdCond, recvguard); - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { if (!m_config.bSynRecving) { @@ -6096,7 +6095,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (m_config.iRcvTimeOut < 0) { THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) { // Do not block forever, check connection status each 1 sec. rcond.wait_for(seconds_from(1)); @@ -6108,7 +6107,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) const steady_clock::time_point exptime = steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) { if (!rcond.wait_until(exptime)) // NOT means "not received a signal" break; // timeout @@ -6122,7 +6121,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { // See at the beginning if (!m_config.bMessageAPI && m_bShutdown) @@ -6152,7 +6151,7 @@ int srt::CUDT::receiveBuffer(char *data, int len) HLOGP(tslog.Debug, "NOT pinging TSBPD - not set"); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -6607,6 +6606,23 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) return receiveBuffer(data, len); } +size_t srt::CUDT::getAvailRcvBufferSizeLock() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->getAvailBufSize(); +} + +size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const +{ + return m_pRcvBuffer->getAvailBufSize(); +} + +bool srt::CUDT::isRcvBufferReady() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->isRcvDataReady(); +} + // int by_exception: accepts values of CUDTUnited::ErrorHandling: // - 0 - by return value // - 1 - by exception @@ -6647,7 +6663,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality"); enterCS(m_RcvBufferLock); - int res = m_pRcvBuffer->readMsg(data, len); + const int res = m_pRcvBuffer->readMsg(data, len); leaveCS(m_RcvBufferLock); w_mctrl.srctime = 0; @@ -6662,7 +6678,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ HLOGP(tslog.Debug, "NOT pinging TSBPD - not set"); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -6713,7 +6729,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // Kick TsbPd thread to schedule next wakeup (if running) if (m_bTsbPd) @@ -6792,12 +6808,12 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGP(tslog.Debug, "receiveMessage: DATA COND: KICKED."); } - } while (stillConnected() && !timeout && (!m_pRcvBuffer->isRcvDataReady())); + } while (stillConnected() && !timeout && (!isRcvBufferReady())); THREAD_RESUMED(); HLOGC(tslog.Debug, log << CONID() << "receiveMessage: lock-waiting loop exited: stillConntected=" << stillConnected() - << " timeout=" << timeout << " data-ready=" << m_pRcvBuffer->isRcvDataReady()); + << " timeout=" << timeout << " data-ready=" << isRcvBufferReady()); } /* XXX DEBUG STUFF - enable when required @@ -6829,7 +6845,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ } } while ((res == 0) && !timeout); - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // Falling here means usually that res == 0 && timeout == true. // res == 0 would repeat the above loop, unless there was also a timeout. @@ -6990,7 +7006,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo { if (!m_bConnected || !m_CongCtl.ready()) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + else if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (!m_config.bMessageAPI && m_bShutdown) return 0; @@ -7072,14 +7088,14 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo CSync rcond (m_RecvDataCond, recvguard); THREAD_PAUSED(); - while (stillConnected() && !m_pRcvBuffer->isRcvDataReady()) + while (stillConnected() && !isRcvBufferReady()) rcond.wait(); THREAD_RESUMED(); } if (!m_bConnected) throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); - else if ((m_bBroken || m_bClosing) && !m_pRcvBuffer->isRcvDataReady()) + else if ((m_bBroken || m_bClosing) && !isRcvBufferReady()) { if (!m_config.bMessageAPI && m_bShutdown) return 0; @@ -7098,7 +7114,7 @@ int64_t srt::CUDT::recvfile(fstream &ofs, int64_t &offset, int64_t size, int blo } } - if (!m_pRcvBuffer->isRcvDataReady()) + if (!isRcvBufferReady()) { // read is not available any more uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, false); @@ -7239,7 +7255,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_pRcvBuffer) { - perf->byteAvailRcvBuf = m_pRcvBuffer->getAvailBufSize() * m_config.iMSS; + perf->byteAvailRcvBuf = getAvailRcvBufferSizeLock() * m_config.iMSS; if (instantaneous) // no need for historical API for Rcv side { perf->pktRcvBuf = m_pRcvBuffer->getRcvDataSize(perf->byteRcvBuf, perf->msRcvBuf); @@ -7489,7 +7505,7 @@ void srt::CUDT::releaseSynch() // [[using locked(m_RcvBufferLock)]]; int32_t srt::CUDT::ackDataUpTo(int32_t ack) { - int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); + const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << ack << " vs. current %" << m_iRcvLastSkipAck << " (signing off " << acksize << " packets)"); @@ -7691,7 +7707,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; + int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; @@ -7881,7 +7897,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) data[ACKD_RCVLASTACK] = m_iRcvLastAck; data[ACKD_RTT] = m_iSRTT; data[ACKD_RTTVAR] = m_iRTTVar; - data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + data[ACKD_BUFFERLEFT] = getAvailRcvBufferSizeNoLock(); // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock if (data[ACKD_BUFFERLEFT] < 2) data[ACKD_BUFFERLEFT] = 2; @@ -9666,7 +9682,7 @@ int srt::CUDT::processData(CUnit* in_unit) continue; } - const int avail_bufsize = m_pRcvBuffer->getAvailBufSize(); + const int avail_bufsize = getAvailRcvBufferSizeNoLock(); if (offset >= avail_bufsize) { // This is already a sequence discrepancy. Probably there could be found @@ -9773,7 +9789,7 @@ int srt::CUDT::processData(CUnit* in_unit) LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset << " BUFr=" << avail_bufsize - << " avail=" << m_pRcvBuffer->getAvailBufSize() + << " avail=" << getAvailRcvBufferSizeNoLock() << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) @@ -11123,7 +11139,7 @@ void srt::CUDT::addEPoll(const int eid) return; enterCS(m_RecvLock); - if (m_pRcvBuffer->isRcvDataReady()) + if (isRcvBufferReady()) { uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } diff --git a/srtcore/core.h b/srtcore/core.h index e74c58c6e..2bbc7b069 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -692,6 +692,9 @@ class CUDT return m_stats.tsStartTime; } + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + bool isRcvBufferReady() const; + // TSBPD thread main function. static void* tsbpd(void* param); @@ -863,17 +866,17 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data - CRcvBuffer* m_pRcvBuffer; // Receiver buffer - CRcvLossList* m_pRcvLossList; // Receiver loss list - std::deque m_FreshLoss; // Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. - int m_iReorderTolerance; // Current value of dynamic reorder tolerance - int m_iConsecEarlyDelivery; // Increases with every OOO packet that came m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. + int m_iReorderTolerance; //< Current value of dynamic reorder tolerance + int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came m_ACKWindow; // ACK history window CPktTimeWindow<16, 64> m_RcvTimeWindow; // Packet arrival time window - int32_t m_iRcvLastAck; // Last sent ACK + int32_t m_iRcvLastAck; // First unacknowledged packet seqno sent in the latest ACK. #ifdef ENABLE_LOGGING int32_t m_iDebugPrevLastAck; #endif @@ -921,7 +924,7 @@ class CUDT sync::Condition m_SendBlockCond; // used to block "send" call sync::Mutex m_SendBlockLock; // lock associated to m_SendBlockCond - sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer + mutable sync::Mutex m_RcvBufferLock; // Protects the state of the m_pRcvBuffer // Protects access to m_iSndCurrSeqNo, m_iSndLastAck sync::Mutex m_RecvAckLock; // Protects the state changes while processing incomming ACK (SRT_EPOLL_OUT) @@ -1033,6 +1036,15 @@ class CUDT int32_t ackDataUpTo(int32_t seq); void handleKeepalive(const char* data, size_t lenghth); + /// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer. + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + size_t getAvailRcvBufferSizeLock() const; + + /// Retrieves the available size of the receiver buffer. + /// Expects that m_RcvBufferLock is locked. + SRT_ATTR_REQUIRES(m_RcvBufferLock) + size_t getAvailRcvBufferSizeNoLock() const; + private: // Trace struct CoreStats { diff --git a/srtcore/sync.h b/srtcore/sync.h index 7cb0f63a9..b68061cf6 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -357,27 +357,25 @@ class SRT_ATTR_SCOPED_CAPABILITY ScopedLock class SRT_ATTR_SCOPED_CAPABILITY UniqueLock { friend class SyncEvent; + int m_iLocked; + Mutex& m_Mutex; public: - SRT_ATTR_ACQUIRE(m_Mutex) + SRT_ATTR_ACQUIRE(m) UniqueLock(Mutex &m); - SRT_ATTR_RELEASE(m_Mutex) + SRT_ATTR_RELEASE() ~UniqueLock(); public: - SRT_ATTR_ACQUIRE(m_Mutex) + SRT_ATTR_ACQUIRE() void lock(); - SRT_ATTR_RELEASE(m_Mutex) + SRT_ATTR_RELEASE() void unlock(); SRT_ATTR_RETURN_CAPABILITY(m_Mutex) Mutex* mutex(); // reflects C++11 unique_lock::mutex() - -private: - int m_iLocked; - Mutex& m_Mutex; }; #endif // ENABLE_STDCXX_SYNC From ac6ec62a9071b0f7c4410b211a9005792e5d3394 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 11 Oct 2021 17:06:27 +0200 Subject: [PATCH 181/683] [tests] Improved the TestConnection.Multiple test. Using std::array. Try to bind SRT directly instead of UDP. --- srtcore/channel.cpp | 2 +- test/test_many_connections.cpp | 80 +++++++++++++++------------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 92fd6b991..85c39ba95 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -584,7 +584,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const if (dcounter > 8) { // Make a random number in the range between 8 and 24 - const int rnd = srt::sync::getRandomInt(8, 24); + const int rnd = srt::sync::genRandomInt(8, 24); if (dcounter > rnd) { diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index cca42ecf0..99bc5925c 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -1,8 +1,10 @@ #define _CRT_RAND_S // For Windows, rand_s #include +#include #include #include +#include #ifdef _WIN32 #include @@ -49,40 +51,36 @@ class TestConnection m_sa.sin_family = AF_INET; m_sa.sin_addr.s_addr = INADDR_ANY; - m_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - ASSERT_NE(m_udp_sock, -1); - // Find unused a port not used by any other service. - // Otherwise srt_connect may actually connect. - int bind_res = -1; + m_server_sock = srt_create_socket(); + ASSERT_NE(m_server_sock, SRT_INVALID_SOCK); + + // Find a port not used by another service. + int bind_res = 0; const sockaddr* psa = reinterpret_cast(&m_sa); - for (int port = 5000; port <= 5555; ++port) + for (int port = 5000; port <= 5100; ++port) { m_sa.sin_port = htons(port); - bind_res = ::bind(m_udp_sock, psa, sizeof m_sa); - if (bind_res >= 0) + + bind_res = srt_bind(m_server_sock, psa, sizeof m_sa); + if (bind_res == 0) { cerr << "Running test on port " << port << "\n"; break; } } - // Close the socket to free the port. - ASSERT_NE(closesocket(m_udp_sock), -1); - ASSERT_GE(bind_res, 0); + ASSERT_GE(bind_res, 0) << "srt_bind returned " << bind_res << ": " << srt_getlasterror_str(); ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1); // Fill the buffer with random data - time_t now; - time(&now); - unsigned int randseed = now % 255; - srand(randseed); - for (int i = 0; i < SRT_LIVE_DEF_PLSIZE; ++i) - buf[i] = rand_r(&randseed) % 255; + std::random_device rnd_device; + std::mt19937 gen(rnd_device()); + std::uniform_int_distribution dis(-128, 127); + std::generate(m_buf.begin(), m_buf.end(), [dis, gen]() mutable { return (char)dis(gen); }); - m_server_sock = srt_create_socket(); + cout << "Generated: " << static_cast(m_buf[0]) << ", " << static_cast(m_buf[1]) << std::endl; - ASSERT_NE(srt_bind(m_server_sock, psa, sizeof m_sa), -1); ASSERT_NE(srt_listen(m_server_sock, NSOCK), -1); } @@ -93,7 +91,6 @@ class TestConnection void AcceptLoop() { - //cerr << "[T] Accepting connections\n"; for (;;) { sockaddr_any addr; @@ -102,18 +99,16 @@ class TestConnection if (acp == -1) { cerr << "[T] Accept error at " << m_accepted.size() - << "/" << NSOCK << ": " << srt_getlasterror_str() << endl; + << "/" << NSOCK << ": " << srt_getlasterror_str() << endl; break; } - //cerr << "[T] Got new acp @" << acp << endl; m_accepted.push_back(acp); } - m_accept_exit = true; - cerr << "[T] Closing those accepted ones\n"; + m_accept_exit = true; - for (auto s: m_accepted) + for (const auto s : m_accepted) { srt_close(s); } @@ -122,12 +117,11 @@ class TestConnection } protected: - SOCKET m_udp_sock = INVALID_SOCKET; sockaddr_in m_sa = sockaddr_in(); SRTSOCKET m_server_sock = SRT_INVALID_SOCK; vector m_accepted; - char buf[SRT_LIVE_DEF_PLSIZE]; - SRTSOCKET srt_socket_list[NSOCK]; + std::array m_buf; + SRTSOCKET m_connections[NSOCK]; volatile bool m_accept_exit = false; }; @@ -135,55 +129,53 @@ class TestConnection TEST_F(TestConnection, Multiple) { - size_t size = SRT_LIVE_DEF_PLSIZE; - - sockaddr_in lsa = m_sa; - + const sockaddr_in lsa = m_sa; const sockaddr* psa = reinterpret_cast(&lsa); auto ex = std::async([this] { return AcceptLoop(); }); - cout << "Opening " << NSOCK << " connections\n"; + cerr << "Opening " << NSOCK << " connections\n"; for (size_t i = 0; i < NSOCK; i++) { - srt_socket_list[i] = srt_create_socket(); + m_connections[i] = srt_create_socket(); + EXPECT_NE(m_connections[i], SRT_INVALID_SOCK); // Give it 60s timeout, many platforms fail to process // so many connections in a short time. int conntimeo = 60; - srt_setsockflag(srt_socket_list[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); + srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n"; //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl; - ASSERT_NE(srt_connect(srt_socket_list[i], psa, sizeof lsa), SRT_ERROR); + ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR); // Set now async sending so that sending isn't blocked int no = 0; - ASSERT_NE(srt_setsockflag(srt_socket_list[i], SRTO_SNDSYN, &no, sizeof no), -1); + ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); } for (size_t j = 1; j <= 100; j++) { for (size_t i = 0; i < NSOCK; i++) { - EXPECT_GT(srt_send(srt_socket_list[i], buf, size), 0); + EXPECT_GT(srt_send(m_connections[i], m_buf.data(), (int) m_buf.size()), 0); } } - cout << "Sending finished, closing caller sockets\n"; + cerr << "Sending finished, closing caller sockets\n"; for (size_t i = 0; i < NSOCK; i++) { - EXPECT_EQ(srt_close(srt_socket_list[i]), SRT_SUCCESS); + EXPECT_EQ(srt_close(m_connections[i]), SRT_SUCCESS); } - // Up to this moment the server sock should survive - cout << "Closing server socket\n"; - - EXPECT_EQ(m_accept_exit, false); + EXPECT_FALSE(m_accept_exit) << "AcceptLoop already broken for some reason!"; + // Up to this moment the server sock should survive + cerr << "Closing server socket\n"; // Close server socket to break the accept loop EXPECT_EQ(srt_close(m_server_sock), 0); + cerr << "Synchronize with the accepting thread\n"; ex.wait(); } From 489c5fc6c6853531d62b9888e0b2bb019c70e358 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Oct 2021 16:01:29 +0200 Subject: [PATCH 182/683] [core] Use Mersenne Twister engine with C++11 instead of the std::random_device. Fixed max value probability for the default (C++03) rand(). --- srtcore/sync.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index aa15c2471..fdf5a0b66 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -283,10 +283,11 @@ bool srt::sync::CGlobEvent::waitForEvent() namespace srt { #if HAVE_CXX11 -static std::random_device& randomDevice() +static std::mt19937& randomGen() { static std::random_device s_RandomDevice; - return s_RandomDevice; + static std::mt19937 s_GenMT19937(s_RandomDevice()); + return s_GenMT19937; } #elif defined(_WIN32) && defined(__MINGW32__) static void initRandSeed() @@ -300,7 +301,7 @@ static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT; static unsigned int genRandSeed() { // Duration::count() does not depend on any global objects, - // therefore it is preferred over count)microseconds(..). + // therefore it is preferred over count_microseconds(..). const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); return (unsigned int) seed; } @@ -325,7 +326,7 @@ int srt::sync::genRandomInt(int minVal, int maxVal) sync::ScopedLock lck(s_mtxRandomDevice); #if HAVE_CXX11 uniform_int_distribution<> dis(minVal, maxVal); - return dis(randomDevice()); + return dis(randomGen()); #else #if defined(__MINGW32__) // No rand_r(..) for MinGW. @@ -342,9 +343,13 @@ int srt::sync::genRandomInt(int minVal, int maxVal) #endif // Map onto [minVal, maxVal]. - // Note. The probablity to get maxVal as the result is minuscule. - const int res = minVal + static_cast((maxVal - minVal) * rand_0_1); - return res; + // Note. There is a minuscule probablity to get maxVal+1 as the result. + // So we have to use long long to handle cases when maxVal = INT32_MAX. + // Also we must check 'res' does not exceed maxVal, + // which may happen if rand_0_1 = 1, even though the chances are low. + const long long llMaxVal = maxVal; + const int res = minVal + static_cast((llMaxVal + 1 - minVal) * rand_0_1); + return min(res, maxVal); #endif // HAVE_CXX11 } From fa1c3736cfd3a76f7892f186955aa57cbaaaa290 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Oct 2021 16:02:14 +0200 Subject: [PATCH 183/683] [tests] Added check for a minimal value of the uniform distribution. --- test/test_many_connections.cpp | 5 +++- test/test_sync.cpp | 50 ++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 99bc5925c..52d9be773 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -126,7 +126,10 @@ class TestConnection }; - +// This test establishes multiple connections to a single SRT listener on a localhost port. +// Packets are submitted for sending to all those connections in a non-blocking mode. +// Then all connections are closed. Some sockets may potentially still have undelivered packets. +// This test tries to reproduce the issue described in #1182, and fixed by #1315. TEST_F(TestConnection, Multiple) { const sockaddr_in lsa = m_sa; diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 6fa59c137..5e08cd30b 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -1,4 +1,5 @@ #include "gtest/gtest.h" +#include #include #include #include @@ -15,14 +16,6 @@ using namespace std; using namespace srt::sync; -// GNUC supports C++14 starting from version 5 -//#if defined(__GNUC__) && (__GNUC__ < 5) -////namespace srt -// constexpr chrono::milliseconds operator"" ms( -// unsigned long long _Val) { // return integral milliseconds -// return chrono::milliseconds(_Val); -//} -//#endif TEST(SyncDuration, BasicChecks) { @@ -159,23 +152,44 @@ TEST(SyncDuration, OperatorMultIntEq) TEST(SyncRandom, GenRandomInt) { - vector mn(64); + array mn = {}; - for (int i = 0; i < 2048; ++i) + // Check generated values are in the specified range. + const size_t n = 2048; + for (size_t i = 0; i < n; ++i) { - const int rand_val = genRandomInt(0, 63); + const int rand_val = genRandomInt(0, mn.size() - 1); ASSERT_GE(rand_val, 0); - ASSERT_LE(rand_val, 63); + ASSERT_LT(rand_val, mn.size()); ++mn[rand_val]; } + // Check the distribution is more or less uniform. + // 100% uniform if each value is generated (n / (2 * mn.size())) times. + // We expect at least half of that value for a random uniform distribution. + const int min_value = n / (2 * mn.size()) - 1; + cout << "min value: " << min_value << endl; + for (size_t i = 0; i < mn.size(); ++i) + { + EXPECT_GE(mn[i], min_value) << "i=" << i << ". Ok-ish if the count is non-zero."; + } + // Uncomment to see the distribution. - // for (size_t i = 0; i < mn.size(); ++i) - // { - // cout << i << '\t'; - // for (int j=0; j Date: Wed, 13 Oct 2021 02:57:21 -0500 Subject: [PATCH 184/683] [build] Fix build with default configuration for GCC <4.7 (#2160) --- CMakeLists.txt | 6 +++++- docs/build/build-options.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21478c36a..0bebae3cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,11 @@ endif() # options option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) -option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) +if (CMAKE_CXX_COMPILER_ID MATCHES "^GNU$" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) + option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" OFF) +else() + option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) +endif() option(ENABLE_APPS "Should the Support Applications be Built?" ON) option(ENABLE_EXPERIMENTAL_BONDING "Should the EXPERIMENTAL bonding functionality be enabled?" OFF) option(ENABLE_TESTING "Should the Developer Test Applications be Built?" OFF) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 938d925d2..d8c1d757a 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -74,7 +74,7 @@ The `pkg-confg` file (`srt.pc`) will be generated with the `libstdc++` library as a dependency. This may be required in some cases where you have an application written in C which therefore won't link against `libstdc++` by default. -**`--enable-c++11`** (default: ON) +**`--enable-c++11`** (default: ON except for GCC<4.7) Enable compiling in C++11 mode for those parts that may require it. Parts that don't require it will still be compiled in C++03 mode, From 2da63dc284aca2a90e4d9a21080cc8bcdd27968e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 16:08:56 +0200 Subject: [PATCH 185/683] [build] Add Android build action to GitHub CI (#2149) Also corrected the 'build-android.sh' script. Co-authored-by: Sergei Ignatov --- .github/workflows/android.yaml | 26 ++++++++++++++++++++++++++ scripts/build-android/build-android | 16 ++++------------ 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/android.yaml diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml new file mode 100644 index 000000000..3cdb0c79e --- /dev/null +++ b/.github/workflows/android.yaml @@ -0,0 +1,26 @@ +name: Android + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: NDK-R23 + runs-on: ubuntu-18.04 + + steps: + - name: Setup Android NDK R23 + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r23 + add-to-path: false + - uses: actions/checkout@v2 + - name: build + run: | + cd ./scripts/build-android/ + echo ${{ steps.setup-ndk.outputs.ndk-path }} + source ./build-android -n ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/scripts/build-android/build-android b/scripts/build-android/build-android index f2d596847..b1c7363a9 100755 --- a/scripts/build-android/build-android +++ b/scripts/build-android/build-android @@ -10,7 +10,6 @@ echo_help() echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls" echo " -o OpenSSL version. E.g. 1.1.1l" echo " -m Mbed TLS version. E.g. v2.26.0" - echo " -s SRT version. E.g. v1.4.4" echo echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\"" echo @@ -21,7 +20,6 @@ NDK_ROOT="" API_LEVEL=28 BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64" OPENSSL_VERSION=1.1.1l -SRT_VERSION="" ENC_LIB=openssl MBEDTLS_VERSION=v2.26.0 @@ -56,10 +54,8 @@ SCRIPT_DIR=$(pwd) HOST_TAG='unknown' unamestr=$(uname -s) if [ "$unamestr" = 'Linux' ]; then - SCRIPT_DIR=$(readlink -f $0 | xargs dirname) HOST_TAG='linux-x86_64' elif [ "$unamestr" = 'Darwin' ]; then - SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) if [ $(uname -p) = 'arm' ]; then echo "NDK does not currently support ARM64" exit 128 @@ -93,12 +89,8 @@ else exit 128 fi -if [ ! -d $BASE_DIR/srt ]; then - git clone https://github.com/Haivision/srt srt - if [ ! -z "$SRT_VERSION" ]; then - git -C $BASE_DIR/srt checkout $SRT_VERSION - fi -fi +# Build working copy of srt repository +REPO_DIR="../.." for build_target in $BUILD_TARGETS; do LIB_DIR=$BASE_DIR/$build_target/lib @@ -113,7 +105,7 @@ for build_target in $BUILD_TARGETS; do cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so fi - git -C $BASE_DIR/srt clean -fd - $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $BASE_DIR/srt -i $BASE_DIR/$build_target + git -C $REPO_DIR clean -fd -e scripts + $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $REPO_DIR -i $BASE_DIR/$build_target cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so done From 8bcf30bfd8f1a85d2147ae930f9eed5c1467076d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 12:43:25 +0200 Subject: [PATCH 186/683] [tests] More RCV buffer unit tests --- test/test_buffer.cpp | 253 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 242 insertions(+), 11 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index d2e7b39f6..a08aa2406 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -1,20 +1,23 @@ #include #include "gtest/gtest.h" #include "buffer.h" +#include using namespace srt; +using namespace std; - +// Check the available size of the receiver buffer. TEST(CRcvBuffer, Create) { const int buffer_size_pkts = 128; CUnitQueue unit_queue; CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic + // One buffer cell is always unavailable as it is use to distinguish + // two buffer states: empty and full. + EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); } - +// Fill the buffer full, and check adding more data results in an error. TEST(CRcvBuffer, FullBuffer) { const int buffer_size_pkts = 16; @@ -51,10 +54,10 @@ TEST(CRcvBuffer, FullBuffer) } } - +// BUG!!! // In this test case a packet is added to receiver buffer with offset 1, // thus leaving offset 0 with an empty pointer. -// The buffer sais it is not empty, and the data is available +// The buffer says it is not empty, and the data is available // to be read, but reading should cause error. TEST(CRcvBuffer, ReadDataIPE) { @@ -73,18 +76,19 @@ TEST(CRcvBuffer, ReadDataIPE) EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); + + // BUG. Acknowledging an empty position must not result in a read-readiness. rcv_buffer.ackData(1); EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - - std::cerr << "Expecting IPE message: \n"; - std::array buff; + cerr << "Expecting IPE message: \n"; + array buff; const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); EXPECT_EQ(res, -1); } - TEST(CRcvBuffer, ReadData) { const int buffer_size_pkts = 16; @@ -112,7 +116,6 @@ TEST(CRcvBuffer, ReadData) EXPECT_EQ(size_t(res), payload_size); } - TEST(CRcvBuffer, AddData) { const int buffer_size_pkts = 16; @@ -159,4 +162,232 @@ TEST(CRcvBuffer, AddData) EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); } +TEST(CRcvBuffer, OneMessageInSeveralPackets) +{ + const int buffer_size_pkts = 16; + CUnitQueue unit_queue; + unit_queue.init(buffer_size_pkts, 1500, AF_INET); + CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); + + const int initial_seqno = 1000; + const int message_len_in_pkts = 4; + const size_t payload_size = 1456; + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < message_len_in_pkts; ++i) + { + CUnit* unit = unit_queue.getNextAvailUnit(); + EXPECT_NE(unit, nullptr); + + CPacket& packet = unit->m_Packet; + packet.setLength(payload_size); + packet.m_iSeqNo = initial_seqno + i; + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + if (i == 0) + packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + const bool is_last_packet = (i == message_len_in_pkts - 1); + if (is_last_packet) + packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + + EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + } + + rcv_buffer.ackData(message_len_in_pkts); + + cout << "Buffer size before reading: " << rcv_buffer.getAvailBufSize() << endl; + std::array buff; + cout << "Reading one packet of the 4-packet message" << endl; + const int res = rcv_buffer.readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, payload_size); + cout << "Buffer size after reading: " << rcv_buffer.getAvailBufSize() << endl; +} + +class TestRcvBufferRead + : public ::testing::Test +{ +protected: + TestRcvBufferRead() + { + // initialization code here + } + + ~TestRcvBufferRead() + { + // cleanup any pending stuff, but no exceptions allowed + } +protected: + // SetUp() is run immediately before a test starts. + void SetUp() override + { + // make_unique is unfortunatelly C++14 + m_unit_queue = unique_ptr(new CUnitQueue); + m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + } + + void TearDown() override + { + // Code here will be called just after the test completes. + // OK to throw exceptions from here if needed. + m_unit_queue.reset(); + m_rcv_buffer.reset(); + } + +public: + void addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false) + { + const int msg_offset = start_seqno - m_init_seqno; + + for (size_t i = 0; i < msg_len_pkts; ++i) + { + CUnit* unit = m_unit_queue->getNextAvailUnit(); + EXPECT_NE(unit, nullptr); + + CPacket& packet = unit->m_Packet; + packet.setLength(m_payload_sz); + packet.m_iSeqNo = start_seqno + i; + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + if (i == 0) + packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + const bool is_last_packet = (i == (msg_len_pkts - 1)); + if (is_last_packet) + packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); + EXPECT_TRUE(packet.getMsgOrderFlag()); + } + + EXPECT_EQ(m_rcv_buffer->addData(unit, msg_offset + i), 0); + } + } + +protected: + unique_ptr m_unit_queue; + unique_ptr m_rcv_buffer; + const int m_buff_size_pkts = 16; + const int m_init_seqno = 1000; + static const size_t m_payload_sz = 1456; +}; + +TEST_F(TestRcvBufferRead, OnePacket) +{ + const size_t msg_pkts = 1; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // Full ACK + m_rcv_buffer->ackData(msg_pkts); + + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +TEST_F(TestRcvBufferRead, OnePacketWithGap) +{ + const size_t msg_pkts = 1; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno + 1, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // ACK first missing packet + m_rcv_buffer->ackData(msg_pkts); + + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + m_rcv_buffer->ackData(msg_pkts); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// Check reading the whole message (consisting of several packets) from the buffer. +TEST_F(TestRcvBufferRead, MsgAcked) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Acknowledge all packets of the message. + m_rcv_buffer->ackData(4); + // Now the whole message can be read. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// BUG!!! +// Checks signalling of read-readiness of a half-acknowledged message. +// The RCV buffer implementation has an issue here: when only half of the message is +// acknowledged, the RCV buffer signals read-readiness, even though +// the message can't be read, and reading returns 0. +TEST_F(TestRcvBufferRead, MsgHalfAck) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + // Nothing to read (0 for zero bytes read). + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); + + // ACK half of the message and check read-readiness. + m_rcv_buffer->ackData(2); + // FIXME: Sadly RCV buffer says the data is ready to be read. + // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + // EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + + // Actually must be nothing to read (can't read half a message). + res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, 0); +} + +// BUG!!! +// Adding a message with the out-of-order flag set. +// RCV buffer does not signal read-readiness, but actually the packet can be read. +TEST_F(TestRcvBufferRead, OutOfOrderMsgNoACK) +{ + const size_t msg_pkts = 4; + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + addMessage(msg_pkts, m_init_seqno, true); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} From 6e896399e3b4fdf09bd11179ecd02145727fd5ee Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 13 Oct 2021 16:07:42 +0200 Subject: [PATCH 187/683] [tests] Fixed async launch TestConnection.Multiple --- test/test_many_connections.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 52d9be773..759b6a461 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -79,8 +79,6 @@ class TestConnection std::uniform_int_distribution dis(-128, 127); std::generate(m_buf.begin(), m_buf.end(), [dis, gen]() mutable { return (char)dis(gen); }); - cout << "Generated: " << static_cast(m_buf[0]) << ", " << static_cast(m_buf[1]) << std::endl; - ASSERT_NE(srt_listen(m_server_sock, NSOCK), -1); } @@ -135,7 +133,7 @@ TEST_F(TestConnection, Multiple) const sockaddr_in lsa = m_sa; const sockaddr* psa = reinterpret_cast(&lsa); - auto ex = std::async([this] { return AcceptLoop(); }); + auto ex = std::async(std::launch::async, [this] { return AcceptLoop(); }); cerr << "Opening " << NSOCK << " connections\n"; @@ -180,6 +178,7 @@ TEST_F(TestConnection, Multiple) cerr << "Synchronize with the accepting thread\n"; ex.wait(); + cerr << "Synchronization done\n"; } From a5b61db5b8e2e6f906665e31d7ec8179175846ed Mon Sep 17 00:00:00 2001 From: Thierry Lelegard Date: Mon, 18 Oct 2021 09:30:06 +0200 Subject: [PATCH 188/683] [build] Fixed missing ws2_32 lib in Windows installer (#2169) The property file libsrt.props did not reference ws2_32.lib. For large applications which already reference the Windows socket library, there is no difference. In small test applications which reference libsrt only, there were undefined symbols. --- scripts/win-installer/libsrt.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/win-installer/libsrt.props b/scripts/win-installer/libsrt.props index 01e0b5dac..b1da74879 100644 --- a/scripts/win-installer/libsrt.props +++ b/scripts/win-installer/libsrt.props @@ -29,7 +29,7 @@ $(LIBSRT)\include;%(AdditionalIncludeDirectories) - srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;%(AdditionalDependencies) + srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;ws2_32.lib;%(AdditionalDependencies) $(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories) /ignore:4099 %(AdditionalOptions) From c1fdb61292358c604cb956fef3d6e768d7e43be6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 18 Oct 2021 15:47:14 +0200 Subject: [PATCH 189/683] [core] Changed lock order in bstats (#2168) --- srtcore/core.cpp | 242 ++++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 120 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b3b1d374c..787533cea 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7130,98 +7130,129 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - ScopedLock statsguard(m_StatsLock); - - const steady_clock::time_point currtime = steady_clock::now(); - - perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); - perf->pktSent = m_stats.traceSent; - perf->pktSentUnique = m_stats.traceSentUniq; - perf->pktRecv = m_stats.traceRecv; - perf->pktRecvUnique = m_stats.traceRecvUniq; - perf->pktSndLoss = m_stats.traceSndLoss; - perf->pktRcvLoss = m_stats.traceRcvLoss; - perf->pktRetrans = m_stats.traceRetrans; - perf->pktRcvRetrans = m_stats.traceRcvRetrans; - perf->pktSentACK = m_stats.sentACK; - perf->pktRecvACK = m_stats.recvACK; - perf->pktSentNAK = m_stats.sentNAK; - perf->pktRecvNAK = m_stats.recvNAK; - perf->usSndDuration = m_stats.sndDuration; - perf->pktReorderDistance = m_stats.traceReorderDistance; - perf->pktReorderTolerance = m_iReorderTolerance; - perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; - perf->pktRcvBelated = m_stats.traceRcvBelated; - - perf->pktSndFilterExtra = m_stats.sndFilterExtra; - perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; - perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; - perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; - - /* perf byte counters include all headers (SRT+UDP+IP) */ const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); - perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); - perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); - perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); - perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); - perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); - - perf->pktSndDrop = m_stats.traceSndDrop; - perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; - perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); - perf->byteRcvDrop = - m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; - perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; - perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; - - perf->pktSentTotal = m_stats.sentTotal; - perf->pktSentUniqueTotal = m_stats.sentUniqTotal; - perf->pktRecvTotal = m_stats.recvTotal; - perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; - perf->pktSndLossTotal = m_stats.sndLossTotal; - perf->pktRcvLossTotal = m_stats.rcvLossTotal; - perf->pktRetransTotal = m_stats.retransTotal; - perf->pktSentACKTotal = m_stats.sentACKTotal; - perf->pktRecvACKTotal = m_stats.recvACKTotal; - perf->pktSentNAKTotal = m_stats.sentNAKTotal; - perf->pktRecvNAKTotal = m_stats.recvNAKTotal; - perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - - perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); - perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); - perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); - perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); - perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); - perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; - perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; - perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; - perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; - - perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); - perf->pktSndDropTotal = m_stats.sndDropTotal; - perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; - perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); - perf->byteRcvDropTotal = - m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; - perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; - perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; - - const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); - perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; - perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; - perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); - perf->pktFlowWindow = m_iFlowWindowSize.load(); - perf->pktCongestionWindow = (int)m_dCongestionWindow; - perf->pktFlightSize = getFlightSpan(); - perf->msRTT = (double)m_iSRTT / 1000.0; - perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; - perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; - perf->byteMSS = m_config.iMSS; - - perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW) - : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) - : 0; + { + ScopedLock statsguard(m_StatsLock); + + const steady_clock::time_point currtime = steady_clock::now(); + + perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); + perf->pktSent = m_stats.traceSent; + perf->pktSentUnique = m_stats.traceSentUniq; + perf->pktRecv = m_stats.traceRecv; + perf->pktRecvUnique = m_stats.traceRecvUniq; + perf->pktSndLoss = m_stats.traceSndLoss; + perf->pktRcvLoss = m_stats.traceRcvLoss; + perf->pktRetrans = m_stats.traceRetrans; + perf->pktRcvRetrans = m_stats.traceRcvRetrans; + perf->pktSentACK = m_stats.sentACK; + perf->pktRecvACK = m_stats.recvACK; + perf->pktSentNAK = m_stats.sentNAK; + perf->pktRecvNAK = m_stats.recvNAK; + perf->usSndDuration = m_stats.sndDuration; + perf->pktReorderDistance = m_stats.traceReorderDistance; + perf->pktReorderTolerance = m_iReorderTolerance; + perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; + perf->pktRcvBelated = m_stats.traceRcvBelated; + + perf->pktSndFilterExtra = m_stats.sndFilterExtra; + perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; + perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; + perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; + + /* perf byte counters include all headers (SRT+UDP+IP) */ + perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); + perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); + perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); + perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); + perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); + perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); + + perf->pktSndDrop = m_stats.traceSndDrop; + perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; + perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); + perf->byteRcvDrop = + m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; + perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; + perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; + + perf->pktSentTotal = m_stats.sentTotal; + perf->pktSentUniqueTotal = m_stats.sentUniqTotal; + perf->pktRecvTotal = m_stats.recvTotal; + perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; + perf->pktSndLossTotal = m_stats.sndLossTotal; + perf->pktRcvLossTotal = m_stats.rcvLossTotal; + perf->pktRetransTotal = m_stats.retransTotal; + perf->pktSentACKTotal = m_stats.sentACKTotal; + perf->pktRecvACKTotal = m_stats.recvACKTotal; + perf->pktSentNAKTotal = m_stats.sentNAKTotal; + perf->pktRecvNAKTotal = m_stats.recvNAKTotal; + perf->usSndDurationTotal = m_stats.m_sndDurationTotal; + + perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); + perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); + perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); + perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); + perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; + perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; + perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; + perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; + + perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); + perf->pktSndDropTotal = m_stats.sndDropTotal; + perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; + perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); + perf->byteRcvDropTotal = + m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; + perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; + perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; + + // TODO: The following class members must be protected with a different mutex, not the m_StatsLock. + const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); + perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; + perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; + perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); + perf->pktFlowWindow = m_iFlowWindowSize.load(); + perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktFlightSize = getFlightSpan(); + perf->msRTT = (double)m_iSRTT / 1000.0; + perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; + perf->msRcvTsbPdDelay = isOPT_TsbPd() ? m_iTsbPdDelay_ms : 0; + perf->byteMSS = m_config.iMSS; + + perf->mbpsMaxBW = m_config.llMaxBW > 0 ? Bps2Mbps(m_config.llMaxBW) + : m_CongCtl.ready() ? Bps2Mbps(m_CongCtl->sndBandwidth()) + : 0; + + if (clear) + { + m_stats.traceSndDrop = 0; + m_stats.traceRcvDrop = 0; + m_stats.traceSndBytesDrop = 0; + m_stats.traceRcvBytesDrop = 0; + m_stats.traceRcvUndecrypt = 0; + m_stats.traceRcvBytesUndecrypt = 0; + m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; + m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; + m_stats.traceSent = m_stats.traceRecv + = m_stats.traceSentUniq = m_stats.traceRecvUniq + = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans + = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; + m_stats.sndDuration = 0; + m_stats.traceRcvRetrans = 0; + m_stats.traceRcvBelated = 0; + m_stats.traceRcvBytesLoss = 0; + + m_stats.sndFilterExtra = 0; + m_stats.rcvFilterExtra = 0; + + m_stats.rcvFilterSupply = 0; + m_stats.rcvFilterLoss = 0; + + m_stats.tsLastSampleTime = currtime; + } + } const int64_t availbw = m_iBandwidth == 1 ? m_RcvTimeWindow.getBandwidth() : m_iBandwidth.load(); @@ -7242,7 +7273,6 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktSndBuf = m_pSndBuffer->getAvgBufSize((perf->byteSndBuf), (perf->msSndBuf)); } perf->byteSndBuf += (perf->pktSndBuf * pktHdrSize); - //< perf->byteAvailSndBuf = (m_config.iSndBufSize - perf->pktSndBuf) * m_config.iMSS; } else @@ -7285,34 +7315,6 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->byteRcvBuf = 0; perf->msRcvBuf = 0; } - - if (clear) - { - m_stats.traceSndDrop = 0; - m_stats.traceRcvDrop = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.traceRcvUndecrypt = 0; - m_stats.traceRcvBytesUndecrypt = 0; - m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; - m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; - m_stats.traceSent = m_stats.traceRecv - = m_stats.traceSentUniq = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.sndDuration = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceRcvBelated = 0; - m_stats.traceRcvBytesLoss = 0; - - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; - - m_stats.tsLastSampleTime = currtime; - } } bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) From 3f2945caff59c50e1e66c5719e473aea5f8ad4e0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 18 Oct 2021 18:45:28 +0200 Subject: [PATCH 190/683] [core] Reduced nesting of checkBrokenSockets() --- srtcore/api.cpp | 106 ++++++++++++++++++++++-------------------------- srtcore/core.h | 3 +- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e35d637a5..59399a4b3 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2572,10 +2572,6 @@ void srt::CUDTUnited::checkBrokenSockets() { ScopedLock cg(m_GlobControlLock); - // set of sockets To Be Closed and To Be Removed - vector tbc; - vector tbr; - #if ENABLE_EXPERIMENTAL_BONDING vector delgids; @@ -2600,74 +2596,70 @@ void srt::CUDTUnited::checkBrokenSockets() { m_ClosedGroups.erase(*di); } - #endif - for (sockets_t::iterator i = m_Sockets.begin(); - i != m_Sockets.end(); ++ i) + // set of sockets To Be Closed and To Be Removed + vector tbc; + vector tbr; + + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++ i) { - CUDTSocket* s = i->second; + CUDTSocket* s = i->second; + if (!s->core().m_bBroken) + continue; - // check broken connection - if (s->core().m_bBroken) + if (s->m_Status == SRTS_LISTENING) { - if (s->m_Status == SRTS_LISTENING) - { - const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; - // for a listening socket, it should wait an extra 3 seconds - // in case a client is connecting - if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) + const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; + // A listening socket should wait an extra 3 seconds + // in case a client is connecting. + if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) + continue; + } + else if ((s->core().m_pRcvBuffer != NULL) + // FIXED: calling isRcvDataAvailable() just to get the information + // whether there are any data waiting in the buffer, + // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when + // this function is called (isRcvDataReady also checks if the + // available data is "ready to play"). + && s->core().m_pRcvBuffer->isRcvDataAvailable()) + { + const int bc = s->core().m_iBrokenCounter.load(); + if (bc > 0) { + // if there is still data in the receiver buffer, wait longer + s->core().m_iBrokenCounter.store(bc - 1); continue; } - } - else if ((s->core().m_pRcvBuffer != NULL) - // FIXED: calling isRcvDataAvailable() just to get the information - // whether there are any data waiting in the buffer, - // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when - // this function is called (isRcvDataReady also checks if the - // available data is "ready to play"). - && s->core().m_pRcvBuffer->isRcvDataAvailable()) - { - const int bc = s->core().m_iBrokenCounter.load(); - if (bc > 0) - { - // HLOGF(smlog.Debug, "STILL KEEPING socket (still have data): - // %d\n", i->first); - // if there is still data in the receiver buffer, wait longer - s->core().m_iBrokenCounter.store(bc - 1); - continue; - } - } + } #if ENABLE_EXPERIMENTAL_BONDING - if (s->m_GroupOf) - { - LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); - s->removeFromGroup(true); - } + if (s->m_GroupOf) + { + LOGC(smlog.Note, log << "@" << s->m_SocketID << " IS MEMBER OF $" << s->m_GroupOf->id() << " - REMOVING FROM GROUP"); + s->removeFromGroup(true); + } #endif - HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); + HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); - //close broken connections and start removal timer - s->setClosed(); - tbc.push_back(i->first); - m_ClosedSockets[i->first] = s; + //close broken connections and start removal timer + s->setClosed(); + tbc.push_back(i->first); + m_ClosedSockets[i->first] = s; - // remove from listener's queue - sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); - if (ls == m_Sockets.end()) - { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } - - enterCS(ls->second->m_AcceptLock); - ls->second->m_QueuedSockets.erase(s->m_SocketID); - leaveCS(ls->second->m_AcceptLock); + // remove from listener's queue + sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(s->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; } + + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); } for (sockets_t::iterator j = m_ClosedSockets.begin(); diff --git a/srtcore/core.h b/srtcore/core.h index 2bbc7b069..523118fce 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -749,7 +749,8 @@ class CUDT sync::atomic m_bPeerHealth; // If the peer status is normal sync::atomic m_RejectReason; bool m_bOpened; // If the UDT entity has been opened - sync::atomic m_iBrokenCounter; // A counter (number of GC checks) to let the GC tag this socket as disconnected + // A counter (number of GC checks happening every 1s) to let the GC tag this socket as closed. + sync::atomic m_iBrokenCounter; // If a broken socket still has data in the receiver buffer, it is not marked closed until the counter is 0. int m_iEXPCount; // Expiration counter sync::atomic m_iBandwidth; // Estimated bandwidth, number of packets per second From 8f2d3182e6c99bdbe3f3af7427b0d610086ea074 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 20 Oct 2021 15:50:17 +0200 Subject: [PATCH 191/683] [tests] Refactored RCV buffer unit tests (#2170) --- srtcore/buffer.cpp | 2 +- test/test_buffer.cpp | 554 +++++++++++++++++++++++-------------------- 2 files changed, 294 insertions(+), 262 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 329b054a0..794066702 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -932,7 +932,7 @@ int CRcvBuffer::readBuffer(char* data, int len) { if (m_pUnit[p] == NULL) { - LOGC(brlog.Error, log << CONID() << " IPE readBuffer on null packet pointer"); + LOGC(brlog.Error, log << CONID() << "IPE readBuffer on null packet pointer"); return -1; } diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index a08aa2406..851fe1167 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -1,267 +1,125 @@ #include +#include #include "gtest/gtest.h" #include "buffer.h" -#include using namespace srt; using namespace std; -// Check the available size of the receiver buffer. -TEST(CRcvBuffer, Create) -{ - const int buffer_size_pkts = 128; - CUnitQueue unit_queue; - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - // One buffer cell is always unavailable as it is use to distinguish - // two buffer states: empty and full. - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); -} - -// Fill the buffer full, and check adding more data results in an error. -TEST(CRcvBuffer, FullBuffer) +class CRcvBufferReadMsg + : public ::testing::Test { - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - for (int i = 0; i < rcv_buffer.getAvailBufSize(); ++i) +protected: + CRcvBufferReadMsg() { - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + // initialization code here } - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic - - rcv_buffer.ackData(buffer_size_pkts - 1); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), 0); - - // Try to add more data than the available size of the buffer - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); - - std::array buff; - for (int i = 0; i < buffer_size_pkts - 1; ++i) + ~CRcvBufferReadMsg() { - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); + // cleanup any pending stuff, but no exceptions allowed } -} - -// BUG!!! -// In this test case a packet is added to receiver buffer with offset 1, -// thus leaving offset 0 with an empty pointer. -// The buffer says it is not empty, and the data is available -// to be read, but reading should cause error. -TEST(CRcvBuffer, ReadDataIPE) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 1), 0); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - - // BUG. Acknowledging an empty position must not result in a read-readiness. - rcv_buffer.ackData(1); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - cerr << "Expecting IPE message: \n"; - array buff; - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(res, -1); -} - -TEST(CRcvBuffer, ReadData) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 0), 0); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - rcv_buffer.ackData(1); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 2); - - std::array buff; - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); -} - -TEST(CRcvBuffer, AddData) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); // logic - - const size_t payload_size = 1456; - // Add 10 units (packets) to the buffer - for (int i = 0; i < 10; ++i) +protected: + // SetUp() is run immediately before a test starts. + void SetUp() override { - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + // make_unique is unfortunatelly C++14 + m_unit_queue = unique_ptr(new CUnitQueue); + ASSERT_NE(m_unit_queue.get(), nullptr); + m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + ASSERT_NE(m_rcv_buffer.get(), nullptr); } - // The available buffer size remains the same - // The value is reported by SRT receiver like this: - // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1); - EXPECT_FALSE(rcv_buffer.isRcvDataAvailable()); - - // Now acknowledge two packets - const int ack_pkts = 2; - rcv_buffer.ackData(2); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - 1 - ack_pkts); - EXPECT_TRUE(rcv_buffer.isRcvDataAvailable()); - - std::array buff; - for (int i = 0; i < ack_pkts; ++i) + void TearDown() override { - const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); - EXPECT_EQ(size_t(res), payload_size); - EXPECT_EQ(rcv_buffer.getAvailBufSize(), buffer_size_pkts - ack_pkts + i); + // Code here will be called just after the test completes. + // OK to throw exceptions from here if needed. + m_unit_queue.reset(); + m_rcv_buffer.reset(); } - // Add packet to the same position - CUnit* unit = unit_queue.getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - unit->m_Packet.setLength(payload_size); - EXPECT_EQ(rcv_buffer.addData(unit, 1), -1); -} - -TEST(CRcvBuffer, OneMessageInSeveralPackets) -{ - const int buffer_size_pkts = 16; - CUnitQueue unit_queue; - unit_queue.init(buffer_size_pkts, 1500, AF_INET); - CRcvBuffer rcv_buffer(&unit_queue, buffer_size_pkts); - - const int initial_seqno = 1000; - const int message_len_in_pkts = 4; - const size_t payload_size = 1456; - // Add a number of units (packets) to the buffer - // equal to the buffer size in packets - for (int i = 0; i < message_len_in_pkts; ++i) +public: + /// Generate and add one packet to the receiver buffer. + /// + /// @returns the result of rcv_buffer::insert(..) + int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) { - CUnit* unit = unit_queue.getNextAvailUnit(); + CUnit* unit = m_unit_queue->getNextAvailUnit(); EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.setLength(payload_size); - packet.m_iSeqNo = initial_seqno + i; + packet.m_iSeqNo = seqno; + packet.m_iTimeStamp = ts; + + packet.setLength(m_payload_sz); + generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); - if (i == 0) + if (pb_first) packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); - const bool is_last_packet = (i == message_len_in_pkts - 1); - if (is_last_packet) + if (pb_last) packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); - EXPECT_EQ(rcv_buffer.addData(unit, i), 0); + if (!out_of_order) + { + packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); + EXPECT_TRUE(packet.getMsgOrderFlag()); + } + + const int offset = CSeqNo::seqoff(m_first_unack_seqno, seqno); + return m_rcv_buffer->addData(unit, offset); } - rcv_buffer.ackData(message_len_in_pkts); + /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed + int addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false, int ts = 0) + { + for (size_t i = 0; i < msg_len_pkts; ++i) + { + const bool pb_first = (i == 0); + const bool pb_last = (i == (msg_len_pkts - 1)); + const int res = addPacket(start_seqno + i, pb_first, pb_last, out_of_order, ts); - cout << "Buffer size before reading: " << rcv_buffer.getAvailBufSize() << endl; - std::array buff; - cout << "Reading one packet of the 4-packet message" << endl; - const int res = rcv_buffer.readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, payload_size); - cout << "Buffer size after reading: " << rcv_buffer.getAvailBufSize() << endl; -} + if (res != 0) + return res; + } + return 0; + } -class TestRcvBufferRead - : public ::testing::Test -{ -protected: - TestRcvBufferRead() + void generatePayload(char* dst, size_t len, int seqno) { std::iota(dst, dst + len, (char)seqno); } + + bool verifyPayload(char* dst, size_t len, int seqno) { - // initialization code here + // Note. A more consistent way would be to use generatePayload function, + // but I don't want to create another buffer for the data. + for (size_t i = 0; i < len; ++i) + { + if (dst[i] != static_cast(seqno + i)) + return false; + } + return true; } - ~TestRcvBufferRead() + int ackPackets(int num_pkts) { - // cleanup any pending stuff, but no exceptions allowed + m_first_unack_seqno = CSeqNo::incseq(m_first_unack_seqno, num_pkts); + return m_rcv_buffer->ackData(num_pkts); } -protected: - // SetUp() is run immediately before a test starts. - void SetUp() override + int getAvailBufferSize() { - // make_unique is unfortunatelly C++14 - m_unit_queue = unique_ptr(new CUnitQueue); - m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); - m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); + return m_rcv_buffer->getAvailBufSize(); } - void TearDown() override + int readMessage(char* data, size_t len) { - // Code here will be called just after the test completes. - // OK to throw exceptions from here if needed. - m_unit_queue.reset(); - m_rcv_buffer.reset(); + return m_rcv_buffer->readMsg(data, len); } -public: - void addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false) + bool hasAvailablePackets() { - const int msg_offset = start_seqno - m_init_seqno; - - for (size_t i = 0; i < msg_len_pkts; ++i) - { - CUnit* unit = m_unit_queue->getNextAvailUnit(); - EXPECT_NE(unit, nullptr); - - CPacket& packet = unit->m_Packet; - packet.setLength(m_payload_sz); - packet.m_iSeqNo = start_seqno + i; - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); - if (i == 0) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); - const bool is_last_packet = (i == (msg_len_pkts - 1)); - if (is_last_packet) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); - - if (!out_of_order) - { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); - EXPECT_TRUE(packet.getMsgOrderFlag()); - } - - EXPECT_EQ(m_rcv_buffer->addData(unit, msg_offset + i), 0); - } + return m_rcv_buffer->isRcvDataAvailable(); } protected: @@ -269,63 +127,149 @@ class TestRcvBufferRead unique_ptr m_rcv_buffer; const int m_buff_size_pkts = 16; const int m_init_seqno = 1000; + int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; }; -TEST_F(TestRcvBufferRead, OnePacket) +// Check the available size of the receiver buffer. +TEST_F(CRcvBufferReadMsg, Create) { - const size_t msg_pkts = 1; - // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno, false); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); +} - const size_t msg_bytelen = msg_pkts * m_payload_sz; - array buff; +// Fill the buffer full, and check adding more data results in an error. +TEST_F(CRcvBufferReadMsg, FullBuffer) +{ + auto& rcv_buffer = *m_rcv_buffer.get(); + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < getAvailBufferSize(); ++i) + { + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + } + + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); // logic - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + ackPackets(m_buff_size_pkts - 1); + EXPECT_EQ(getAvailBufferSize(), 0); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + // Try to add more data than the available size of the buffer + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, getAvailBufferSize())), -1); + + array buff; + for (int i = 0; i < m_buff_size_pkts - 1; ++i) + { + const int res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } +} + +// BUG in the new RCV buffer!!! +// In this test case a packet is added to receiver buffer with offset 1, +// thus leaving offset 0 with an empty pointer. +// The buffer says it is not empty, and the data is available +// to be read, but reading is not possible. +TEST_F(CRcvBufferReadMsg, OnePacketGap) +{ + // Add one packet message to to the buffer + // with a gap of one packet. + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0); + + auto& rcv_buffer = *m_rcv_buffer.get(); + // Before ACK the available buffer size stays the same. + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + // Not available for reading as not yet acknowledged. + EXPECT_FALSE(hasAvailablePackets()); + // Confirm reading zero bytes. + array buff; + int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); - // Full ACK - m_rcv_buffer->ackData(msg_pkts); + // BUG. Acknowledging an empty position must not result in a read-readiness. + ackPackets(1); + // Wrong behavior (BUG) + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 2); + cerr << "Expecting IPE from readBuffer(..): \n"; + res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_EQ(res, -1); - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, 0); } -TEST_F(TestRcvBufferRead, OnePacketWithGap) +// Add one packet to the buffer and read it once it is acknowledged. +// Confirm the data read is valid. +TEST_F(CRcvBufferReadMsg, OnePacket) { const size_t msg_pkts = 1; // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno + 1, false); + addMessage(msg_pkts, m_init_seqno, false); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_FALSE(hasAvailablePackets()); + const int res1 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res1, 0); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + // Full ACK + ackPackets(msg_pkts); + EXPECT_TRUE(hasAvailablePackets()); - // ACK first missing packet - m_rcv_buffer->ackData(msg_pkts); + const int res2 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res2, msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); +} - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); +// Add ten packets to the buffer, acknowledge and read some of them. +// Then try to add packets to the position of existing packets. +// We can't check adding to the position of those packets already read, +// because a negative offset is not checked by the receiver buffer, +// but must be handled by the CUDT socket. +TEST_F(CRcvBufferReadMsg, AddData) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + } - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_FALSE(hasAvailablePackets()); - m_rcv_buffer->ackData(msg_pkts); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + // Now acknowledge two packets + const int ack_pkts = 2; + ackPackets(2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts); + EXPECT_TRUE(hasAvailablePackets()); - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + std::array buff; + for (int i = 0; i < ack_pkts; ++i) + { + const int res = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } + + // Add packet to the position of oackets already read. + // Can't check, as negative offset is an error not handled by the receiver buffer. + //EXPECT_EQ(addPacket(m_init_seqno), -1); + + // Add packet to a non-empty position. + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); } // Check reading the whole message (consisting of several packets) from the buffer. -TEST_F(TestRcvBufferRead, MsgAcked) +TEST_F(CRcvBufferReadMsg, MsgAcked) { const size_t msg_pkts = 4; // Adding one message without acknowledging @@ -335,12 +279,47 @@ TEST_F(TestRcvBufferRead, MsgAcked) array buff; // Acknowledge all packets of the message. - m_rcv_buffer->ackData(4); + ackPackets(msg_pkts); // Now the whole message can be read. EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); - const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +} + +// Check reading the whole message (consisting of several packets) into +// a buffer of an insufficient size. +TEST_F(CRcvBufferReadMsg, SmallReadBuffer) +{ + const size_t msg_pkts = 4; + // Adding one message without acknowledging + addMessage(msg_pkts, m_init_seqno, false); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Acknowledge all packets of the message. + ackPackets(msg_pkts); + // Now the whole message can be read. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + // Check reading into an insufficient size buffer. + // The old buffer extracts the whole message, but copies only + // the number of bytes provided in the 'len' argument. + const int res = readMessage(buff.data(), m_payload_sz); + EXPECT_EQ(res, 1456); + + // No more messages to read + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); } // BUG!!! @@ -348,7 +327,7 @@ TEST_F(TestRcvBufferRead, MsgAcked) // The RCV buffer implementation has an issue here: when only half of the message is // acknowledged, the RCV buffer signals read-readiness, even though // the message can't be read, and reading returns 0. -TEST_F(TestRcvBufferRead, MsgHalfAck) +TEST_F(CRcvBufferReadMsg, MsgHalfAck) { const size_t msg_pkts = 4; // Adding one message without acknowledging @@ -358,36 +337,89 @@ TEST_F(TestRcvBufferRead, MsgHalfAck) const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); - int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + EXPECT_FALSE(hasAvailablePackets()); + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); // ACK half of the message and check read-readiness. - m_rcv_buffer->ackData(2); + ackPackets(2); // FIXME: Sadly RCV buffer says the data is ready to be read. // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - // EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + // EXPECT_FALSE(hasAvailablePackets()); EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); - EXPECT_TRUE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_TRUE(hasAvailablePackets()); // Actually must be nothing to read (can't read half a message). - res = m_rcv_buffer->readMsg(buff.data(), buff.size()); - EXPECT_EQ(res, 0); + const int res2 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res2, 0); } // BUG!!! // Adding a message with the out-of-order flag set. // RCV buffer does not signal read-readiness, but actually the packet can be read. -TEST_F(TestRcvBufferRead, OutOfOrderMsgNoACK) +TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) { const size_t msg_pkts = 4; // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, m_init_seqno, true); EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - EXPECT_FALSE(m_rcv_buffer->isRcvDataAvailable()); + EXPECT_FALSE(hasAvailablePackets()); + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); +} + +// Adding a message with the out-of-order flag set. +// The message can be read. +TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) +{ + const size_t msg_pkts = 4; + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + addMessage(msg_pkts, CSeqNo::incseq(m_init_seqno, 1), true); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; - const int res = m_rcv_buffer->readMsg(buff.data(), buff.size()); + const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, 1 + i))); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + // Adding one message with the Out-Of-Order flag set, but without acknowledging. + //int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) + const int res2 = addPacket(CSeqNo::incseq(m_init_seqno, 1)); + EXPECT_EQ(res2, -1); // already exists + + EXPECT_EQ(addPacket(m_init_seqno), 0); + ackPackets(msg_pkts + 1); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res3 = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(res3 == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, m_init_seqno)); + + // Only "passack" packets remain in the buffer. + // They are falsely signalled as read-ready. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); // BUG: nothing to read. + EXPECT_TRUE(hasAvailablePackets()); // BUG: nothing to read. + + // Adding a packet right after the EntryState_Read packets. + const int seqno = CSeqNo::incseq(m_init_seqno, msg_pkts + 1); + EXPECT_EQ(addPacket(seqno), 0); + ackPackets(1); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); } From 276a841da020eadf7da304cf03e1d62c63341748 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 22 Oct 2021 14:04:06 +0200 Subject: [PATCH 192/683] [core] New receiver buffer implementation --- CMakeLists.txt | 11 +- docs/build/build-options.md | 5 + srtcore/api.cpp | 6 + srtcore/buffer.cpp | 4 + srtcore/buffer.h | 4 + srtcore/buffer_rcv.cpp | 969 ++++++++++++++++++++++++++++++++++++ srtcore/buffer_rcv.h | 343 +++++++++++++ srtcore/core.cpp | 282 ++++++++++- srtcore/core.h | 9 + srtcore/filelist.maf | 2 + srtcore/queue.h | 1 + srtcore/utilities.h | 64 +++ 12 files changed, 1683 insertions(+), 17 deletions(-) create mode 100644 srtcore/buffer_rcv.cpp create mode 100644 srtcore/buffer_rcv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bebae3cd..77286fb7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -204,7 +205,6 @@ if (NOT USE_ENCLIB) else() set (USE_ENCLIB openssl) endif() - endif() set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") @@ -486,6 +486,14 @@ if (ENABLE_SOCK_CLOEXEC) add_definitions(-DENABLE_SOCK_CLOEXEC=1) endif() +if (ENABLE_NEW_RCVBUFFER) + add_definitions(-DENABLE_NEW_RCVBUFFER=1) + message(STATUS "RECEIVER_BUFFER: NEW") +else() + remove_definitions(-DENABLE_NEW_RCVBUFFER) + message(STATUS "RECEIVER_BUFFER: OLD") +endif() + if (CMAKE_MAJOR_VERSION LESS 3) set (FORCE_CXX_STANDARD_GNUONLY 1) endif() @@ -1291,6 +1299,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) endif() MafReadDir(test filelist.maf + HEADERS SOURCES_unittests SOURCES SOURCES_unittests ) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index d8c1d757a..3afce1cf3 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -192,6 +192,11 @@ will be removed when the problem is fixed globally. This option enables the standard C++ `thread` and `chrono` libraries (available since C++11) to be used by SRT instead of the `pthreads`. +**`--enable-new-rcvbuffer`** (default: ON) + +This option enables the new implementation of the receiver buffer with behavior and code improvements. +The new receiver buffer is to remain the only one. For the transition period there is a possibility to +fall back to the old one. **`--enable-profile`** (default: OFF) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 59399a4b3..8e18bd23e 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -151,8 +151,10 @@ void srt::CUDTSocket::setBrokenClosed() bool srt::CUDTSocket::readReady() { + // TODO: Use m_RcvBufferLock here (CUDT::isRcvReadReady())? if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) return true; + if (m_UDT.m_bListening) return !m_QueuedSockets.empty(); @@ -2622,7 +2624,11 @@ void srt::CUDTUnited::checkBrokenSockets() // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when // this function is called (isRcvDataReady also checks if the // available data is "ready to play"). +#if ENABLE_NEW_RCVBUFFER + && s->core().m_pRcvBuffer->hasAvailablePackets()) +#else && s->core().m_pRcvBuffer->isRcvDataAvailable()) +#endif { const int bc = s->core().m_iBrokenCounter.load(); if (bc > 0) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 794066702..e861654eb 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -792,6 +792,8 @@ void CSndBuffer::increase() //////////////////////////////////////////////////////////////////////////////// +#if (!ENABLE_NEW_RCVBUFFER) + /* * RcvBuffer (circular buffer): * @@ -2289,4 +2291,6 @@ bool CRcvBuffer::scanMsg(int& w_p, int& w_q, bool& w_passack) return found; } +#endif // !ENABLE_NEW_RCVBUFFER + } // namespace srt diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 8756a49d1..428db748d 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -272,6 +272,8 @@ class CSndBuffer //////////////////////////////////////////////////////////////////////////////// +#if (!ENABLE_NEW_RCVBUFFER) + class CRcvBuffer { typedef sync::steady_clock::time_point time_point; @@ -562,6 +564,8 @@ class CRcvBuffer CRcvBuffer& operator=(const CRcvBuffer&); }; +#endif // !ENABLE_NEW_RCVBUFFER + } // namespace srt #endif diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp new file mode 100644 index 000000000..12e935057 --- /dev/null +++ b/srtcore/buffer_rcv.cpp @@ -0,0 +1,969 @@ +#if ENABLE_NEW_RCVBUFFER +#include +#include "buffer_rcv.h" +#include "logging.h" + +using namespace std; + +using namespace srt::sync; +using namespace srt_logging; +namespace srt_logging +{ + extern Logger brlog; +} +#define rbuflog brlog + +namespace srt { + +namespace { + struct ScopedLog + { + ScopedLog() {}; + + ~ScopedLog() + { + LOGC(rbuflog.Warn, log << ss.str()); + } + + stringstream ss; + }; + +#define IF_RCVBUF_DEBUG(instr) (void)0 + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const bool isOverrun = iLastPos < iStartPos; + + if (isOverrun) + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + } +} + + +/* + * RcvBufferNew (circular buffer): + * + * |<------------------- m_iSize ----------------------------->| + * | |<----------- m_iMaxPosInc ------------>| | + * | | | | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | | + * | |__last pkt received + * |___ m_iStartPos: first message to read + * + * m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped + * + * thread safety: + * m_iStartPos: CUDT::m_RecvLock + * m_iLastAckPos: CUDT::m_AckLock + * m_iMaxPosInc: none? (modified on add and ack + */ + +CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI) + : m_entries(size) + , m_szSize(size) // TODO: maybe just use m_entries.size() + , m_pUnitQueue(unitqueue) + , m_iStartSeqNo(initSeqNo) + , m_iStartPos(0) + , m_iFirstNonreadPos(0) + , m_iMaxPosInc(0) + , m_iNotch(0) + , m_numOutOfOrderPackets(0) + , m_iFirstReadableOutOfOrder(-1) + , m_bPeerRexmitFlag(peerRexmit) + , m_bMessageAPI(bMessageAPI) + , m_iBytesCount(0) + , m_iPktsCount(0) + , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) +{ + SRT_ASSERT(size < INT_MAX); // All position pointers are integers +} + +CRcvBufferNew::~CRcvBufferNew() +{ + for (size_t i = 0; i < m_szSize; ++i) + { + CUnit* unit = m_entries[i].pUnit; + if (unit != NULL) + { + m_pUnitQueue->makeUnitFree(unit); + m_entries[i].pUnit = NULL; + } + } +} + +int CRcvBufferNew::insert(CUnit* unit) +{ + SRT_ASSERT(unit != NULL); + const int32_t seqno = unit->m_Packet.getSeqNo(); + const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + + if (offset < 0) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); + return -2; + } + + if (offset >= (int)capacity()) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); + return -3; + } + + // TODO: Don't do assert here. Process this situation somehow. + // If >= 2, then probably there is a long gap, and buffer needs to be reset. + SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); + + const int pos = (m_iStartPos + offset) % m_szSize; + if (offset >= m_iMaxPosInc) + m_iMaxPosInc = offset + 1; + + // Packet already exists + SRT_ASSERT(pos >= 0 && pos < m_szSize); + if (m_entries[pos].status != EntryState_Empty) + { + IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); + return -1; + } + SRT_ASSERT(m_entries[pos].pUnit == NULL); + + m_pUnitQueue->makeUnitGood(unit); + m_entries[pos].pUnit = unit; + m_entries[pos].status = EntryState_Avail; + countBytes(1, (int)unit->m_Packet.getLength()); + + // If packet "in order" flag is zero, it can be read out of order. + // With TSBPD enabled packets are always assumed in order (the flag is ignored). + if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) + { + ++m_numOutOfOrderPackets; + onInsertNotInOrderPacket(pos); + } + + updateNonreadPos(); + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); + return 0; +} + +void CRcvBufferNew::dropUpTo(int32_t seqno) +{ + // Can drop only when nothing to read, and + // first unacknowledged packet is missing. + SRT_ASSERT(m_iStartPos == m_iFirstNonreadPos); + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); + + int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + SRT_ASSERT(len > 0); + if (len <= 0) + { + IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); + return; + } + + /*LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropUpTo(): seqno=" << seqno << ", pkts=" << len + << ". Buffer start " << m_iStartSeqNo << ".");*/ + + m_iMaxPosInc -= len; + if (m_iMaxPosInc < 0) + m_iMaxPosInc = 0; + + // Check that all packets being dropped are missing. + while (len > 0) + { + if (m_entries[m_iStartPos].pUnit != NULL) + { + releaseUnitInPos(m_iStartPos); + } + + if (m_entries[m_iStartPos].status != EntryState_Empty) + { + SRT_ASSERT(m_entries[m_iStartPos].status == EntryState_Drop || m_entries[m_iStartPos].status == EntryState_Read); + m_entries[m_iStartPos].status = EntryState_Empty; + } + + SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); + m_iStartPos = incPos(m_iStartPos); + --len; + } + + // Update positions + m_iStartSeqNo = seqno; + // Move forward if there are "read" entries. + releaseNextFillerEntries(); + // Set nonread position to the starting position before updating, + // because start position was increased, and preceeding packets are invalid. + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); +} + +void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) +{ + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); + // TODO: count bytes as removed? + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + if (msgno != 0) + { + for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + { + // TODO: Maybe check status? + if (!m_entries[i].pUnit) + continue; + + const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); + if (msgseq == msgno) + { + releaseUnitInPos(i); + m_entries[i].status = EntryState_Drop; + } + } + + return; + } + + // Drop by packet seqno range. + const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); + const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + if (offset_b < 0) + { + LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " + << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + return; + } + + const int start_off = max(0, offset_a); + const int last_pos = incPos(m_iStartPos, offset_b); + for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) + { + if (m_entries[i].pUnit) + { + releaseUnitInPos(i); + } + m_entries[i].status = EntryState_Drop; + } + + LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; " + << seqnohi << "]."); +} + +int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +{ + const bool canReadInOrder = hasReadableInorderPkts(); + if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + { + LOGC(rbuflog.Warn, log << "CRcvBufferNew.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); + return 0; + } + + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); + + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + // Remember if we actually read out of order packet. + const bool readingOutOfOrderPacket = !canReadInOrder || m_iStartPos == m_iFirstReadableOutOfOrder; + + size_t remain = len; + char* dst = data; + int pkts_read = 0; + int bytes_extracted = 0; // The total number of bytes extracted from the buffer. + const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed + for (int i = readPos;; i = incPos(i)) + { + SRT_ASSERT(m_entries[i].pUnit); + if (!m_entries[i].pUnit) + { + LOGC(rbuflog.Error, log << "CRcvBufferNew::readMessage(): null packet encountered."); + break; + } + + const CPacket& packet = m_entries[i].pUnit->m_Packet; + const size_t pktsize = packet.getLength(); + const int32_t pktseqno = packet.getSeqNo(); + + // unitsize can be zero + const size_t unitsize = std::min(remain, pktsize); + memcpy(dst, packet.m_pcData, unitsize); + remain -= unitsize; + dst += unitsize; + + ++pkts_read; + bytes_extracted += (int) pktsize; + + if (m_tsbpd.isEnabled()) + updateTsbPdTimeBase(packet.getMsgTimeStamp()); + + if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) + --m_numOutOfOrderPackets; + + const bool pbLast = packet.getMsgBoundary() & PB_LAST; + if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) + { + msgctrl->pktseq = pktseqno; + msgctrl->msgno = packet.getMsgSeq(m_bPeerRexmitFlag); + } + if (msgctrl && pbLast) + { + msgctrl->srctime = count_microseconds(getPktTsbPdTime(packet.getMsgTimeStamp()).time_since_epoch()); + } + + releaseUnitInPos(i); + if (updateStartPos) + { + m_iStartPos = incPos(i); + --m_iMaxPosInc; + SRT_ASSERT(m_iMaxPosInc >= 0); + m_iStartSeqNo = CSeqNo::incseq(pktseqno); + } + else + { + // If out of order, only mark it read. + m_entries[i].status = EntryState_Read; + } + + if (pbLast) + { + if (readPos == m_iFirstReadableOutOfOrder) + m_iFirstReadableOutOfOrder = -1; + break; + } + } + + countBytes(-pkts_read, -bytes_extracted); + if (!m_tsbpd.isEnabled() && readingOutOfOrderPacket) + updateFirstReadableOutOfOrder(); + + releaseNextFillerEntries(); + + if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + { + m_iFirstNonreadPos = m_iStartPos; + //updateNonreadPos(); + } + + const int bytes_read = dst - data; + if (bytes_read < bytes_extracted) + { + LOGC(rbuflog.Error, log << "readMessage: small dst buffer, copied only " << bytes_read << "/" << bytes_extracted << " bytes."); + } + + return bytes_read; +} + +namespace { + /// @brief Writes bytes to file stream. + /// @param data pointer to data to write. + /// @param len the number of bytes to write + /// @param arg a void pointer to the fstream to write to. + /// @return true on success, false on failure + bool writeBytesToFile(char* data, int len, void* arg) + { + fstream* pofs = reinterpret_cast(arg); + pofs->write(data, len); + return !pofs->fail(); + } + + /// @brief Copies bytes to the destination buffer. + /// @param data pointer to data to copy. + /// @param len the number of bytes to copy + /// @param arg A pointer to the destination buffer + /// @return true on success, false on failure + bool copyBytesToBuf(char* data, int len, void* arg) + { + char* dst = reinterpret_cast(arg); + memcpy(dst, data, len); + return true; + } +} + +int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) +{ + int p = m_iStartPos; + const int end_pos = m_iFirstNonreadPos; + + const bool bTsbPdEnabled = m_tsbpd.isEnabled(); + const steady_clock::time_point now = (bTsbPdEnabled ? steady_clock::now() : steady_clock::time_point()); + + int rs = len; + while ((p != end_pos) && (rs > 0)) + { + if (!m_entries[p].pUnit) + { + p = incPos(p); + LOGC(rbuflog.Error, log << "readBufferTo: IPE: NULL unit found in file transmission"); + return -1; + } + + const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + + if (bTsbPdEnabled) + { + const steady_clock::time_point tsPlay = getPktTsbPdTime(pkt.getMsgTimeStamp()); + HLOGC(rbuflog.Debug, + log << "readBuffer: check if time to play:" + << " NOW=" << FormatTime(now) + << " PKT TS=" << FormatTime(tsPlay)); + + if ((tsPlay > now)) + break; /* too early for this unit, return whatever was copied */ + } + + const int pktlen = (int)pkt.getLength(); + const int remain_pktlen = pktlen - m_iNotch; + const int unitsize = std::min(remain_pktlen, rs); + + if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, arg)) + break; + + if (rs >= remain_pktlen) + { + releaseUnitInPos(p); + p = incPos(p); + m_iNotch = 0; + + m_iStartPos = p; + --m_iMaxPosInc; + SRT_ASSERT(m_iMaxPosInc >= 0); + m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + } + else + m_iNotch += rs; + + rs -= unitsize; + } + + const int iBytesRead = len - rs; + /* we removed acked bytes form receive buffer */ + countBytes(-1, -iBytesRead); + + // Update positions + // Set nonread position to the starting position before updating, + // because start position was increased, and preceeding packets are invalid. + if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + { + m_iFirstNonreadPos = m_iStartPos; + } + + if (iBytesRead == 0) + { + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + } + + return iBytesRead; +} + +int CRcvBufferNew::readBuffer(char* dst, int len) +{ + return readBufferTo(len, copyBytesToBuf, reinterpret_cast(dst)); +} + +int CRcvBufferNew::readBufferToFile(fstream& ofs, int len) +{ + return readBufferTo(len, writeBytesToFile, reinterpret_cast(&ofs)); +} + +bool CRcvBufferNew::hasAvailablePackets() const +{ + return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); +} + +int CRcvBufferNew::getRcvDataSize() const +{ + if (m_iFirstNonreadPos >= m_iStartPos) + return m_iFirstNonreadPos - m_iStartPos; + + return m_szSize + m_iFirstNonreadPos - m_iStartPos; +} + +int CRcvBufferNew::getTimespan_ms() const +{ + if (!m_tsbpd.isEnabled()) + return 0; + + if (m_iMaxPosInc == 0) + return 0; + + const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); + int startpos = m_iStartPos; + + while (m_entries[startpos].pUnit == NULL) + { + if (startpos == lastpos) + break; + + ++startpos; + } + + if (m_entries[startpos].pUnit == NULL) + return 0; + + // Should not happen + SRT_ASSERT(m_entries[lastpos].pUnit != NULL); + if (m_entries[lastpos].pUnit == NULL) + return 0; + + const steady_clock::time_point startstamp = + getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + if (endstamp < startstamp) + return 0; + + // One millisecond is added as a duration of a packet in the buffer. + // If there is only one packet in the buffer, one millisecond is returned. + return count_milliseconds(endstamp - startstamp) + 1; +} + +int CRcvBufferNew::getRcvDataSize(int& bytes, int& timespan) const +{ + ScopedLock lck(m_BytesCountLock); + bytes = m_iBytesCount; + timespan = getTimespan_ms(); + return m_iPktsCount; +} + +CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstValidPacketInfo() const +{ + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + { + // TODO: Maybe check status? + if (!m_entries[i].pUnit) + continue; + + const CPacket& packet = m_entries[i].pUnit->m_Packet; + const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; + return info; + } + + const PacketInfo info = { -1, false, time_point() }; + return info; +} + +std::pair CRcvBufferNew::getAvailablePacketsRange() const +{ + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, countReadable()); + return std::pair(m_iStartSeqNo, seqno_last); +} + +size_t CRcvBufferNew::countReadable() const +{ + if (m_iFirstNonreadPos >= m_iStartPos) + return m_iFirstNonreadPos - m_iStartPos; + return m_szSize + m_iFirstNonreadPos - m_iStartPos; +} + +bool CRcvBufferNew::isRcvDataReady(time_point time_now) const +{ + const bool haveInorderPackets = hasReadableInorderPkts(); + if (!m_tsbpd.isEnabled()) + { + if (haveInorderPackets) + return true; + + SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); + return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + } + + if (!haveInorderPackets) + return false; + + const PacketInfo info = getFirstValidPacketInfo(); + + return info.tsbpd_time <= time_now; +} + +void CRcvBufferNew::countBytes(int pkts, int bytes) +{ + ScopedLock lock(m_BytesCountLock); + m_iBytesCount += bytes; // added or removed bytes from rcv buffer + m_iPktsCount += pkts; + if (bytes > 0) // Assuming one pkt when adding bytes + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); +} + +void CRcvBufferNew::releaseUnitInPos(int pos) +{ + CUnit* tmp = m_entries[pos].pUnit; + m_entries[pos] = Entry(); // pUnit = NULL; status = Empty + if (tmp != NULL) + m_pUnitQueue->makeUnitFree(tmp); +} + +void CRcvBufferNew::releaseNextFillerEntries() +{ + int pos = m_iStartPos; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) + { + m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + releaseUnitInPos(pos); + pos = incPos(pos); + m_iStartPos = pos; + } +} + +// TODO: Is this function complete? There are some comments left inside. +void CRcvBufferNew::updateNonreadPos() +{ + if (m_iMaxPosInc == 0) + return; + + // const PacketBoundary boundary = packet.getMsgBoundary(); + + //// The simplest case is when inserting a sequential PB_SOLO packet. + // if (boundary == PB_SOLO && (m_iFirstNonreadPos + 1) % m_szSize == pos) + //{ + // m_iFirstNonreadPos = pos; + // return; + //} + const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + + int pos = m_iFirstNonreadPos; + while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST)) + { + // bool good = true; + + // look ahead for the whole message + + // We expect to see either of: + // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] + // [PB_SOLO] + // but not: + // [PB_FIRST] NULL ... + // [PB_FIRST] FREE/PASSACK/DROPPED... + // If the message didn't look as expected, interrupt this. + + // This begins with a message starting at m_iStartPos + // up to end_pos (excluding) OR until the PB_LAST message is found. + // If any of the units on this way isn't good, this OUTER loop + // will be interrupted. + for (int i = pos; i != end_pos; i = (i + 1) % m_szSize) + { + if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) + { + // good = false; + break; + } + + // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. + if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + { + m_iFirstNonreadPos = incPos(i); + break; + } + } + + if (pos == m_iFirstNonreadPos || !m_entries[m_iFirstNonreadPos].pUnit) + break; + + pos = m_iFirstNonreadPos; + } + + // 1. If there is a gap between this packet and m_iLastReadablePos + // then no sense to update m_iLastReadablePos. + + // 2. The simplest case is when this is the first sequential packet +} + +int CRcvBufferNew::findLastMessagePkt() +{ + for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + { + SRT_ASSERT(m_entries[i].pUnit); + + if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + { + return i; + } + } + + return -1; +} + +void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) +{ + if (m_numOutOfOrderPackets == 0) + return; + + // If the following condition is true, there is already a packet, + // that can be read out of order. We don't need to search for + // another one. The search should be done when that packet is read out from the buffer. + // + // There might happen that the packet being added precedes the previously found one. + // However, it is allowed to re bead out of order, so no need to update the position. + if (m_iFirstReadableOutOfOrder >= 0) + return; + + // Just a sanity check. This function is called when a new packet is added. + // So the should be unacknowledged packets. + SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_entries[insertPos].pUnit); + const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const PacketBoundary boundary = pkt.getMsgBoundary(); + + //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) + //{ + // // This packet can be read out of order + // m_iFirstReadableOutOfOrder = insertPos; + // return; + //} + + const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); + // First check last packet, because it is expected to be received last. + const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + if (!hasLast) + return; + + const int firstPktPos = (boundary & PB_FIRST) + ? insertPos + : scanNotInOrderMessageLeft(insertPos, msgNo); + if (firstPktPos < 0) + return; + + m_iFirstReadableOutOfOrder = firstPktPos; + return; +} + +void CRcvBufferNew::updateFirstReadableOutOfOrder() +{ + if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + return; + + if (m_iMaxPosInc == 0) + return; + + // TODO: unused variable outOfOrderPktsRemain? + int outOfOrderPktsRemain = m_numOutOfOrderPackets; + + // Search further packets to the right. + // First check if there are packets to the right. + const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + + int posFirst = -1; + int posLast = -1; + int msgNo = -1; + + for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + { + if (!m_entries[pos].pUnit) + { + posFirst = posLast = msgNo = -1; + continue; + } + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgOrderFlag()) // Skip in order packet + { + posFirst = posLast = msgNo = -1; + continue; + } + + --outOfOrderPktsRemain; + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_FIRST) + { + posFirst = pos; + msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); + } + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + posFirst = posLast = msgNo = -1; + continue; + } + + if (boundary & PB_LAST) + { + m_iFirstReadableOutOfOrder = posFirst; + return; + } + + if (pos == lastPos) + break; + } + + return; +} + +int CRcvBufferNew::scanNotInOrderMessageRight(const int startPos, int msgNo) const +{ + // Search further packets to the right. + // First check if there are packets to the right. + const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + if (startPos == lastPos) + return -1; + + int pos = startPos; + do + { + pos = incPos(pos); + if (!m_entries[pos].pUnit) + break; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); + return -1; + } + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_LAST) + return pos; + } while (pos != lastPos); + + return -1; +} + +int CRcvBufferNew::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +{ + // Search preceeding packets to the left. + // First check if there are packets to the left. + if (startPos == m_iStartPos) + return -1; + + int pos = startPos; + do + { + pos = decPos(pos); + + if (!m_entries[pos].pUnit) + return -1; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + + if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) + { + LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); + return -1; + } + + const PacketBoundary boundary = pkt.getMsgBoundary(); + if (boundary & PB_FIRST) + return pos; + } while (pos != m_iStartPos); + + return -1; +} + +bool CRcvBufferNew::addRcvTsbPdDriftSample(uint32_t usTimestamp, int usRTTSample) +{ + return m_tsbpd.addDriftSample(usTimestamp, usRTTSample); +} + +void CRcvBufferNew::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay) +{ + m_tsbpd.setTsbPdMode(timebase, wrap, delay); +} + +void CRcvBufferNew::applyGroupTime(const steady_clock::time_point& timebase, + bool wrp, + uint32_t delay, + const steady_clock::duration& udrift) +{ + m_tsbpd.applyGroupTime(timebase, wrp, delay, udrift); +} + +void CRcvBufferNew::applyGroupDrift(const steady_clock::time_point& timebase, + bool wrp, + const steady_clock::duration& udrift) +{ + m_tsbpd.applyGroupDrift(timebase, wrp, udrift); +} + +CRcvBufferNew::time_point CRcvBufferNew::getTsbPdTimeBase(uint32_t usPktTimestamp) const +{ + return m_tsbpd.getTsbPdTimeBase(usPktTimestamp); +} + +void CRcvBufferNew::updateTsbPdTimeBase(uint32_t usPktTimestamp) +{ + m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); +} + +string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +{ + stringstream ss; + + ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize; + ss << " pkts. "; + if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + { + ss << " (TSBPD ready in "; + if (m_entries[m_iStartPos].pUnit) + { + const uint32_t usPktTimestamp = m_entries[m_iStartPos].pUnit->m_Packet.getMsgTimeStamp(); + ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - tsNow); + } + else + { + ss << "n/a"; + } + + const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + if (m_entries[iLastPos].pUnit) + { + ss << ":"; + const uint32_t usPktTimestamp = m_entries[m_iStartPos].pUnit->m_Packet.getMsgTimeStamp(); + ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - tsNow); + ss << " ms"; + } + else + { + ss << ":n/a ms"; + } + } + + ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + return ss.str(); +} + +CRcvBufferNew::time_point CRcvBufferNew::getPktTsbPdTime(uint32_t usPktTimestamp) const +{ + return m_tsbpd.getPktTsbPdTime(usPktTimestamp); +} + +/* Return moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +int CRcvBufferNew::getRcvAvgDataSize(int& bytes, int& timespan) +{ + // Average number of packets and timespan could be small, + // so rounding is beneficial, while for the number of + // bytes in the buffer is a higher value, so rounding can be omitted, + // but probably better to round all three values. + timespan = static_cast(round((m_mavg.timespan_ms()))); + bytes = static_cast(round((m_mavg.bytes()))); + return static_cast(round(m_mavg.pkts())); +} + +/* Update moving average of acked data pkts, bytes, and timespan (ms) of the receive buffer */ +void CRcvBufferNew::updRcvAvgDataSize(const steady_clock::time_point& now) +{ + if (!m_mavg.isTimeToUpdate(now)) + return; + + int bytes = 0; + int timespan_ms = 0; + const int pkts = getRcvDataSize(bytes, timespan_ms); + m_mavg.update(now, pkts, bytes, timespan_ms); +} + +} // namespace srt + +#endif // ENABLE_NEW_RCVBUFFER diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h new file mode 100644 index 000000000..dfc63d3f6 --- /dev/null +++ b/srtcore/buffer_rcv.h @@ -0,0 +1,343 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2020 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_BUFFER_RCV_H +#define INC_SRT_BUFFER_RCV_H + +#if ENABLE_NEW_RCVBUFFER + +#include "buffer.h" // AvgBufSize +#include "common.h" +#include "queue.h" +#include "sync.h" +#include "tsbpd_time.h" + +namespace srt +{ + +/* + * Circular receiver buffer. + * + * |<------------------- m_szSize ---------------------------->| + * | |<------------ m_iMaxPosInc ----------->| | + * | | | | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ + * | | + * | \__last pkt received + * | + * \___ m_iStartPos: first message to read + * + * m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) + * + * thread safety: + * start_pos_: CUDT::m_RecvLock + * first_unack_pos_: CUDT::m_AckLock + * max_pos_inc_: none? (modified on add and ack + * first_nonread_pos_: + */ + +class CRcvBufferNew +{ + typedef sync::steady_clock::time_point time_point; + typedef sync::steady_clock::duration duration; + +public: + CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI); + + ~CRcvBufferNew(); + +public: + /// Insert a unit into the buffer. + /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + /// + /// @param [in] unit pointer to a data unit containing new packet + /// @param [in] offset offset from last ACK point. + /// + /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. + /// -3 if a packet is offset is ahead the buffer capacity. + // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. + int insert(CUnit* unit); + + /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). + /// @param [in] seqno drop units up to this sequence number + /// + void dropUpTo(int32_t seqno); + + /// @brief Drop the whole message from the buffer. + /// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. + /// When one packet of the message is in the range of dropping, the whole message is to be dropped. + /// @param seqnolo sequence number of the first packet in the dropping range. + /// @param seqnohi sequence number of the last packet in the dropping range. + /// @param msgno message number to drop (0 if unknown) + void dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + + /// Read the whole message from one or several packets. + /// + /// @param [in,out] data buffer to write the message into. + /// @param [in] len size of the buffer. + /// @param [in,out] message control data + /// + /// @return actual number of bytes extracted from the buffer. + /// 0 if nothing to read. + /// -1 on failure. + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + + /// Read acknowledged data into a user buffer. + /// @param [in, out] dst pointer to the target user buffer. + /// @param [in] len length of user buffer. + /// @return size of data read. -1 on error. + int readBuffer(char* dst, int len); + + /// Read acknowledged data directly into file. + /// @param [in] ofs C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. -1 on error. + int readBufferToFile(std::fstream& ofs, int len); + +public: + /// Get the starting position of the buffer as a packet sequence number. + int getStartSeqNo() const { return m_iStartSeqNo; } + + /// Given the sequence number of the first unacknowledged packet + /// tells the size of the buffer available for packets. + /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. + // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. + size_t getAvailSize(int iFirstUnackSeqNo) const + { + // Receiver buffer allows reading unacknowledged packets. + // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo + // then it does not have acknowledged packets and its full capacity is available. + // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. + const int iRBufSeqNo = getStartSeqNo(); + if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + { + // Full capacity is available, still don't want to encourage extra packets to come. + // Note: CSeqNo::seqlen(n, n) returns 1. + return capacity() - CSeqNo::seqlen(iFirstUnackSeqNo, iRBufSeqNo) + 1; + } + + // Note: CSeqNo::seqlen(n, n) returns 1. + return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; + } + + /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. + /// @return true if there are packets available for reading, false otherwise. + bool hasAvailablePackets() const; + + /// Query how many data has been continuously received (for reading) and available for reading out + /// regardless of the TSBPD. + /// TODO: Rename to countAvailablePackets(). + /// @return size of valid (continous) data for reading. + int getRcvDataSize() const; + + /// Get the number of packets, bytes and buffer timespan. + /// Differs from getRcvDataSize() that it counts all packets in the buffer, not only continious. + int getRcvDataSize(int& bytes, int& timespan) const; + + struct PacketInfo + { + int seqno; + bool seq_gap; //< true if there are missing packets in the buffer, preceding current packet + time_point tsbpd_time; + }; + + /// Get information on the 1st message in queue. + /// Similar to CRcvBuffer::getRcvFirstMsg + /// Parameters (of the 1st packet queue, ready to play or not): + /// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if + /// none + /// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app) + /// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets. + /// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true + /// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE: + /// IF skipseqno != -1, packet ready to play preceeded by missing packets.; + /// IF skipseqno == -1, no missing packet but 1st not ready to play. + PacketInfo getFirstValidPacketInfo() const; + + /// Get information on the packets available to be read + /// @returns a pair of sequence numbers + std::pair getAvailablePacketsRange() const; + + size_t countReadable() const; + + bool empty() const + { + return (m_iMaxPosInc == 0); + } + + /// Return buffer capacity. + /// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer". + /// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously. + /// TODO: Old receiver buffer capacity returns the actual size. Check for conflicts. + size_t capacity() const + { + return m_szSize - 1; + } + + int64_t getDrift() const { return m_tsbpd.drift(); } + + // TODO: make thread safe? + int debugGetSize() const + { + return getRcvDataSize(); + } + + /// Zero time to include all available packets. + /// TODO: Rename to 'canRead`. + bool isRcvDataReady(time_point time_now = time_point()) const; + + int getRcvAvgDataSize(int& bytes, int& timespan); + void updRcvAvgDataSize(const time_point& now); + + unsigned getRcvAvgPayloadSize() const { return m_uAvgPayloadSz; } + + void getInternalTimeBase(time_point& w_timebase, bool& w_wrp, duration& w_udrift) + { + return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift); + } + +public: // Used for testing + /// Peek unit in position of seqno + const CUnit* peek(int32_t seqno); + +private: + inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } + inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } + +private: + void countBytes(int pkts, int bytes); + void updateNonreadPos(); + void releaseUnitInPos(int pos); + + /// Release entries following the current buffer position if they were already + /// read out of order (EntryState_Read) or dropped (EntryState_Drop). + void releaseNextFillerEntries(); + + bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } + + /// Find position of the last packet of the message. + int findLastMessagePkt(); + + /// Scan for availability of out of order packets. + void onInsertNotInOrderPacket(int insertpos); + void updateFirstReadableOutOfOrder(); + int scanNotInOrderMessageRight(int startPos, int msgNo) const; + int scanNotInOrderMessageLeft(int startPos, int msgNo) const; + + typedef bool copy_to_dst_f(char* data, int len, void* arg); + + /// Read acknowledged data directly into file. + /// @param [in] ofs C++ file stream. + /// @param [in] len expected length of data to write into the file. + /// @return size of data read. + int readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg); + + /// @brief Estimate timespan of the stored packets (acknowledged and unacknowledged). + /// @return timespan in milliseconds + int getTimespan_ms() const; + +private: + // TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing. + // TODO: CUnitPtr is not in use at the moment, but may be a smart pointer. + // class CUnitPtr + // { + // public: + // void operator=(CUnit* pUnit) + // { + // if (m_pUnit != NULL) + // { + // // m_pUnitQueue->makeUnitFree(m_entries[i].pUnit); + // } + // m_pUnit = pUnit; + // } + // private: + // CUnit* m_pUnit; + // }; + + enum EntryStatus + { + EntryState_Empty, //< No CUnit record. + EntryState_Avail, //< Entry is available for reading. + EntryState_Read, //< Entry has already been read (out of order). + EntryState_Drop //< Entry has been dropped. + }; + struct Entry + { + Entry() + : pUnit(NULL) + , status(EntryState_Empty) + {} + + CUnit* pUnit; + EntryStatus status; + }; + + //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } + + FixedArray m_entries; + + const size_t m_szSize; // size of the array of units (buffer) + CUnitQueue* m_pUnitQueue; // the shared unit queue + + int m_iStartSeqNo; + int m_iStartPos; // the head position for I/O (inclusive) + int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + int m_iMaxPosInc; // the furthest data position + int m_iNotch; // the starting read point of the first unit + + size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false + int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to + // read + const bool m_bPeerRexmitFlag; // Needed to read message number correctly + const bool m_bMessageAPI; // Operation mode flag: message or stream. + +public: // TSBPD public functions + /// Set TimeStamp-Based Packet Delivery Rx Mode + /// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay + /// @param [in] wrap Is in wrapping period + /// @param [in] delay aggreed TsbPD delay + /// + /// @return 0 + void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); + + void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); + + bool addRcvTsbPdDriftSample(uint32_t usTimestamp, int usRTTSample); + + time_point getPktTsbPdTime(uint32_t usPktTimestamp) const; + + time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; + void updateTsbPdTimeBase(uint32_t usPktTimestamp); + + /// Form a string of the current buffer fullness state. + /// number of packets acknowledged, TSBPD readiness, etc. + std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + +private: + CTsbpdTime m_tsbpd; + +private: // Statistics + AvgBufSize m_mavg; + + // TODO: m_BytesCountLock is probably not needed as the buffer has to be protected from simultaneous access. + mutable sync::Mutex m_BytesCountLock; // used to protect counters operations + int m_iBytesCount; // Number of payload bytes in the buffer + int m_iPktsCount; // Number of payload bytes in the buffer + unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation +}; + +} // namespace srt + +#endif // ENABLE_NEW_RCVBUFFER +#endif // INC_SRT_BUFFER_RCV_H diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 787533cea..67c2e6b08 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5166,6 +5166,199 @@ void srt::CUDT::rendezvousSwitchState(UDTRequestType& w_rsptype, bool& w_needs_e * This thread runs only if TsbPd mode is enabled * Hold received packets until its time to 'play' them, at PktTimeStamp + TsbPdDelay. */ +#if ENABLE_NEW_RCVBUFFER +void * srt::CUDT::tsbpd(void* param) +{ + CUDT* self = (CUDT*)param; + + THREAD_STATE_INIT("SRT:TsbPd"); + +#if ENABLE_EXPERIMENTAL_BONDING + // Make the TSBPD thread a "client" of the group, + // which will ensure that the group will not be physically + // deleted until this thread exits. + // NOTE: DO NOT LEAD TO EVER CANCEL THE THREAD!!! + CUDTUnited::GroupKeeper gkeeper(self->uglobal(), self->m_parent); +#endif + + UniqueLock recv_lock(self->m_RecvLock); + CSync recvdata_cc(self->m_RecvDataCond, recv_lock); + CSync tsbpd_cc(self->m_RcvTsbPdCond, recv_lock); + + self->m_bTsbPdAckWakeup = true; + while (!self->m_bClosing) + { + steady_clock::time_point tsNextDelivery; // Next packet delivery time + bool rxready = false; +#if ENABLE_EXPERIMENTAL_BONDING + bool shall_update_group = false; +#endif + + enterCS(self->m_RcvBufferLock); + const steady_clock::time_point tnow = steady_clock::now(); + + self->m_pRcvBuffer->updRcvAvgDataSize(tnow); + const srt::CRcvBufferNew::PacketInfo info = self->m_pRcvBuffer->getFirstValidPacketInfo(); + + const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); + tsNextDelivery = info.tsbpd_time; + + if (!self->m_bTLPktDrop) + { + rxready = !info.seq_gap && is_time_to_deliver; + } + else if (is_time_to_deliver) + { + rxready = true; + if (info.seq_gap) + { + const int seq_gap_len = CSeqNo::seqoff(self->m_iRcvLastSkipAck, info.seqno); + SRT_ASSERT(seq_gap_len > 0); + /*if (!info.seq_gap) + { + LOGC(brlog.Warn, log << "TSBPD worker: no gap. pktseqno=" << info.seqno + << ", m_iRcvLastSkipAck=" << self->m_iRcvLastSkipAck + << ", RBuffer start seqno=" << self->m_pRcvBuffer->getStartSeqNo() + << ", m_iRcvLastAck=" << self->m_iRcvLastAck + << ", init seqnoo=" << self->m_iISN); + }*/ + + // Drop too late packets + self->updateForgotten(seq_gap_len, self->m_iRcvLastSkipAck, info.seqno); + //if (info.seq_gap) // If there is no sequence gap, we are reading ahead of ACK. + //{ + self->m_pRcvBuffer->dropUpTo(info.seqno); + //} + + self->m_iRcvLastSkipAck = info.seqno; +#if ENABLE_EXPERIMENTAL_BONDING + shall_update_group = true; +#endif + +#if ENABLE_LOGGING + const int64_t timediff_us = count_microseconds(tnow - info.tsbpd_time); + // TODO: seq_gap_len is not the actual number of packets dropped. +#if ENABLE_HEAVY_LOGGING + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" + << seq_gap_len << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " + << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); +#endif + LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << seq_gap_len << " packet(s), packet seqno %" << info.seqno + << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') + << (timediff_us % 1000) << " ms"); +#endif + + tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. + } + } + leaveCS(self->m_RcvBufferLock); + + if (rxready) + { + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " + << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + /* + * There are packets ready to be delivered + * signal a waiting "recv" call if there is any data available + */ + if (self->m_config.bSynRecving) + { + recvdata_cc.signal_locked(recv_lock); + } + /* + * Set EPOLL_IN to wakeup any thread waiting on epoll + */ + self->uglobal().m_EPoll.update_events(self->m_SocketID, self->m_sPollID, SRT_EPOLL_IN, true); +#if ENABLE_EXPERIMENTAL_BONDING + // If this is NULL, it means: + // - the socket never was a group member + // - the socket was a group member, but: + // - was just removed as a part of closure + // - and will never be member of the group anymore + + // If this is not NULL, it means: + // - This socket is currently member of the group + // - This socket WAS a member of the group, though possibly removed from it already, BUT: + // - the group that this socket IS OR WAS member of is in the GroupKeeper + // - the GroupKeeper prevents the group from being deleted + // - it is then completely safe to access the group here, + // EVEN IF THE SOCKET THAT WAS ITS MEMBER IS BEING DELETED. + + // It is ensured that the group object exists here because GroupKeeper + // keeps it busy, even if you just closed the socket, remove it as a member + // or even the group is empty and was explicitly closed. + if (gkeeper.group) + { + // Functions called below will lock m_GroupLock, which in hierarchy + // lies after m_RecvLock. Must unlock m_RecvLock to be able to lock + // m_GroupLock inside the calls. + InvertedLock unrecv(self->m_RecvLock); + // The current "APP reader" needs to simply decide as to whether + // the next CUDTGroup::recv() call should return with no blocking or not. + // When the group is read-ready, it should update its pollers as it sees fit. + + // NOTE: this call will set lock to m_IncludedGroup->m_GroupLock + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: GROUP: checking if %" << info.seqno << " makes group readable"); + gkeeper.group->updateReadState(self->m_SocketID, info.seqno); + + if (shall_update_group) + { + // A group may need to update the parallelly used idle links, + // should it have any. Pass the current socket position in order + // to skip it from the group loop. + // NOTE: SELF LOCKING. + gkeeper.group->updateLatestRcv(self->m_parent); + } + } +#endif + CGlobEvent::triggerEvent(); + tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. + } + + if (!is_zero(tsNextDelivery)) + { + IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); + /* + * Buffer at head of queue is not ready to play. + * Schedule wakeup when it will be. + */ + self->m_bTsbPdAckWakeup = false; + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + THREAD_PAUSED(); + tsbpd_cc.wait_until(tsNextDelivery); + THREAD_RESUMED(); + } + else + { + /* + * We have just signaled epoll; or + * receive queue is empty; or + * next buffer to deliver is not in receive queue (missing packet in sequence). + * + * Block until woken up by one of the following event: + * - All ready-to-play packets have been pulled and EPOLL_IN cleared (then loop to block until next pkt time + * if any) + * - New buffers ACKed + * - Closing the connection + */ + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); + self->m_bTsbPdAckWakeup = true; + THREAD_PAUSED(); + tsbpd_cc.wait(); + THREAD_RESUMED(); + } + + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + } + THREAD_EXIT(); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); + return NULL; +} +#else void * srt::CUDT::tsbpd(void *param) { CUDT *self = (CUDT *)param; @@ -5382,6 +5575,7 @@ void * srt::CUDT::tsbpd(void *param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); return NULL; } +#endif // ENABLE_NEW_RCVBUFFER void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { @@ -5435,7 +5629,12 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd try { m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); +#if ENABLE_NEW_RCVBUFFER + SRT_ASSERT(m_iISN != -1); + m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_bPeerRexmitFlag, m_config.bMessageAPI); +#else m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.iRcvBufSize); +#endif // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); m_pRcvLossList = new CRcvLossList(m_config.iFlightFlagSize); @@ -6609,18 +6808,26 @@ int srt::CUDT::recvmsg2(char* data, int len, SRT_MSGCTRL& w_mctrl) size_t srt::CUDT::getAvailRcvBufferSizeLock() const { ScopedLock lck(m_RcvBufferLock); - return m_pRcvBuffer->getAvailBufSize(); + return getAvailRcvBufferSizeNoLock(); } size_t srt::CUDT::getAvailRcvBufferSizeNoLock() const { +#if ENABLE_NEW_RCVBUFFER + return m_pRcvBuffer->getAvailSize(m_iRcvLastAck); +#else return m_pRcvBuffer->getAvailBufSize(); +#endif } bool srt::CUDT::isRcvBufferReady() const { ScopedLock lck(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); +#else return m_pRcvBuffer->isRcvDataReady(); +#endif } // int by_exception: accepts values of CUDTUnited::ErrorHandling: @@ -6663,7 +6870,13 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: CONNECTION BROKEN - reading from recv buffer just for formality"); enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) + : 0; +#else const int res = m_pRcvBuffer->readMsg(data, len); +#endif leaveCS(m_RcvBufferLock); w_mctrl.srctime = 0; @@ -6697,13 +6910,21 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return res; } +#if !ENABLE_NEW_RCVBUFFER const int seqdistance = -1; +#endif if (!m_config.bSynRecving) { HLOGC(arlog.Debug, log << CONID() << "receiveMessage: BEGIN ASYNC MODE. Going to extract payload size=" << len); enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + const int res = (m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + ? m_pRcvBuffer->readMessage(data, len, &w_mctrl) + : 0; +#else const int res = m_pRcvBuffer->readMsg(data, len, (w_mctrl), seqdistance); +#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (NON-BLOCKING) result=" << res); @@ -6765,9 +6986,13 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ do { +#if ENABLE_NEW_RCVBUFFER + if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now())) +#else steady_clock::time_point tstime SRT_ATR_UNUSED; int32_t seqno; if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady((tstime), (seqno), seqdistance)) +#endif { /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) @@ -6780,7 +7005,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ // of kicking TSBPD. // bool spurious = (tstime != 0); - HLOGC(tslog.Debug, log << CONID() << "receiveMessage: KICK tsbpd" << (is_zero(tstime) ? " (SPURIOUS!)" : "")); + HLOGC(tslog.Debug, log << CONID() << "receiveMessage: KICK tsbpd"); tscond.signal_locked(recvguard); } @@ -6823,7 +7048,11 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ */ enterCS(m_RcvBufferLock); +#if ENABLE_NEW_RCVBUFFER + res = m_pRcvBuffer->readMessage((data), len, &w_mctrl); +#else res = m_pRcvBuffer->readMsg((data), len, (w_mctrl), seqdistance); +#endif leaveCS(m_RcvBufferLock); HLOGC(arlog.Debug, log << CONID() << "AFTER readMsg: (BLOCKING) result=" << res); @@ -7509,12 +7738,22 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) { const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); - HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << ack << " vs. current %" << m_iRcvLastSkipAck - << " (signing off " << acksize << " packets)"); + HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << m_iRcvLastSkipAck << " -> %" << ack + << " (" << acksize << " packets)"); m_iRcvLastAck = ack; m_iRcvLastSkipAck = ack; +#if ENABLE_NEW_RCVBUFFER + const std::pair range = m_pRcvBuffer->getAvailablePacketsRange(); + // Some packets acknowledged are not available in the buffer. + if (CSeqNo::seqcmp(range.second, ack) < 0) + { + LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first + << " - %" << range.second); + } + return acksize; +#else // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. @@ -7529,6 +7768,7 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) if (distance > 0) return CSeqNo::decseq(ack, distance); return ack; +#endif } namespace srt { @@ -8644,10 +8884,16 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; + { - const bool using_rexmit_flag = m_bPeerRexmitFlag; UniqueLock rlock(m_RecvLock); + const bool using_rexmit_flag = m_bPeerRexmitFlag; +#if ENABLE_NEW_RCVBUFFER + m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); +#else m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); +#endif // When the drop request was received, it means that there are // packets for which there will never be ACK sent; if the TSBPD thread // is currently in the ACK-waiting state, it may never exit. @@ -8659,8 +8905,6 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } - const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; - dropFromLossLists(dropdata[0], dropdata[1]); // move forward with current recv seq no. @@ -8809,7 +9053,11 @@ void srt::CUDT::updateSrtRcvSettings() { /* We are TsbPd receiver */ enterCS(m_RecvLock); +#if ENABLE_NEW_RCVBUFFER + m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms)); +#else m_pRcvBuffer->setRcvTsbPdMode(m_tsRcvPeerStartTime, milliseconds_from(m_iTsbPdDelay_ms)); +#endif leaveCS(m_RecvLock); HLOGF(cnlog.Debug, @@ -9718,17 +9966,28 @@ int srt::CUDT::processData(CUnit* in_unit) } else { +#if ENABLE_NEW_RCVBUFFER + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); +#else LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo << ", insert offset " << offset << ". " << m_pRcvBuffer->strFullnessState(steady_clock::now()) ); +#endif return -1; } } bool adding_successful = true; +#if ENABLE_NEW_RCVBUFFER + if (m_pRcvBuffer->insert(*i) < 0) +#else if (m_pRcvBuffer->addData(*i, offset) < 0) +#endif { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. // So this packet is "redundant". @@ -9774,14 +10033,6 @@ int srt::CUDT::processData(CUnit* in_unit) } #if ENABLE_HEAVY_LOGGING - std::ostringstream timebufspec; - if (m_bTsbPd) - { - int dsize = m_pRcvBuffer->getRcvDataSize(); - timebufspec << "(" << FormatTime(m_pRcvBuffer->debugGetDeliveryTime(0)) - << "-" << FormatTime(m_pRcvBuffer->debugGetDeliveryTime(dsize-1)) << ")"; - } - std::ostringstream expectspec; if (excessive) expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; @@ -9798,7 +10049,6 @@ int srt::CUDT::processData(CUnit* in_unit) << ") " << " RSL=" << expectspec.str() << " SN=" << rexmitstat[pktrexmitflag] - << " DLVTM=" << timebufspec.str() << " FLAGS: " << rpkt.MessageFlagStr()); #endif diff --git a/srtcore/core.h b/srtcore/core.h index 523118fce..9f957d040 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -60,6 +60,7 @@ modified by #include "common.h" #include "list.h" #include "buffer.h" +#include "buffer_rcv.h" #include "window.h" #include "packet.h" #include "channel.h" @@ -417,7 +418,11 @@ class CUDT SRTU_PROPERTY_RO(SRTSOCKET, id, m_SocketID); SRTU_PROPERTY_RO(bool, isClosing, m_bClosing); +#if ENABLE_NEW_RCVBUFFER + SRTU_PROPERTY_RO(srt::CRcvBufferNew*, rcvBuffer, m_pRcvBuffer); +#else SRTU_PROPERTY_RO(CRcvBuffer*, rcvBuffer, m_pRcvBuffer); +#endif SRTU_PROPERTY_RO(bool, isTLPktDrop, m_bTLPktDrop); SRTU_PROPERTY_RO(bool, isSynReceiving, m_config.bSynRecving); SRTU_PROPERTY_RR(sync::Condition*, recvDataCond, &m_RecvDataCond); @@ -867,7 +872,11 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK private: // Receiving related data +#if ENABLE_NEW_RCVBUFFER + CRcvBufferNew* m_pRcvBuffer; //< Receiver buffer +#else CRcvBuffer* m_pRcvBuffer; //< Receiver buffer +#endif CRcvLossList* m_pRcvLossList; //< Receiver loss list std::deque m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for. int m_iReorderTolerance; //< Current value of dynamic reorder tolerance diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 54e15df62..16fb48d45 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -3,6 +3,7 @@ SOURCES api.cpp buffer.cpp +buffer_rcv.cpp cache.cpp channel.cpp common.cpp @@ -53,6 +54,7 @@ udt.h PRIVATE HEADERS api.h buffer.h +buffer_rcv.h cache.h channel.h common.h diff --git a/srtcore/queue.h b/srtcore/queue.h index 2b7408747..21983d005 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -79,6 +79,7 @@ struct CUnit DROPPED = 3 }; Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped + // TODO: Transition to the new RcvBuffer allows to use bool here. }; class CUnitQueue diff --git a/srtcore/utilities.h b/srtcore/utilities.h index f787a579e..1610505b8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -410,6 +410,70 @@ struct DynamicStruct }; +/// Fixed-size array template class. +namespace srt { + +template +class FixedArray +{ +public: + FixedArray(size_t size) + : m_size(size) + , m_entries(new T[size]) + { + } + + ~FixedArray() + { + delete [] m_entries; + } + +public: + const T& operator[](size_t index) const + { + if (index >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + T& operator[](size_t index) + { + if (index >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + const T& operator[](int index) const + { + if (index < 0 || static_cast(index) >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + T& operator[](int index) + { + if (index < 0 || static_cast(index) >= m_size) + throw std::runtime_error("Invalid index"); + + return m_entries[index]; + } + + size_t size() const { return m_size; } + +private: + FixedArray(const FixedArray& ); + FixedArray& operator=(const FixedArray&); + +private: + size_t m_size; + T* const m_entries; +}; + +} // namespace srt + // ------------------------------------------------------------ From 427ceceff868921c0ebaba07101c55b5e1221b02 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 22 Oct 2021 14:04:27 +0200 Subject: [PATCH 193/683] [tests] Receiver buffer tests --- test/test_buffer.cpp | 430 ++++++++++++++++++++++++++++++++++++++++++- test/test_epoll.cpp | 2 +- 2 files changed, 422 insertions(+), 10 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 851fe1167..896303d59 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -2,6 +2,7 @@ #include #include "gtest/gtest.h" #include "buffer.h" +#include "buffer_rcv.h" using namespace srt; using namespace std; @@ -28,7 +29,14 @@ class CRcvBufferReadMsg m_unit_queue = unique_ptr(new CUnitQueue); ASSERT_NE(m_unit_queue.get(), nullptr); m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); + +#if ENABLE_NEW_RCVBUFFER + const bool enable_msg_api = true; + const bool enable_peer_rexmit = true; + m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); +#else m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); +#endif ASSERT_NE(m_rcv_buffer.get(), nullptr); } @@ -36,8 +44,8 @@ class CRcvBufferReadMsg { // Code here will be called just after the test completes. // OK to throw exceptions from here if needed. - m_unit_queue.reset(); m_rcv_buffer.reset(); + m_unit_queue.reset(); } public: @@ -68,8 +76,12 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->insert(unit); +#else const int offset = CSeqNo::seqoff(m_first_unack_seqno, seqno); return m_rcv_buffer->addData(unit, offset); +#endif } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed @@ -104,31 +116,54 @@ class CRcvBufferReadMsg int ackPackets(int num_pkts) { m_first_unack_seqno = CSeqNo::incseq(m_first_unack_seqno, num_pkts); +#if ENABLE_NEW_RCVBUFFER + return 0; +#else return m_rcv_buffer->ackData(num_pkts); +#endif } int getAvailBufferSize() { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->getAvailSize(m_first_unack_seqno); +#else return m_rcv_buffer->getAvailBufSize(); +#endif } int readMessage(char* data, size_t len) { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->readMessage(data, len); +#else return m_rcv_buffer->readMsg(data, len); +#endif } bool hasAvailablePackets() { +#if ENABLE_NEW_RCVBUFFER + return m_rcv_buffer->hasAvailablePackets(); +#else return m_rcv_buffer->isRcvDataAvailable(); +#endif } protected: unique_ptr m_unit_queue; +#if ENABLE_NEW_RCVBUFFER + unique_ptr m_rcv_buffer; +#else unique_ptr m_rcv_buffer; +#endif const int m_buff_size_pkts = 16; const int m_init_seqno = 1000; int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; + + const sync::steady_clock::time_point m_tsbpd_base = sync::steady_clock::now(); // now() - HS.timestamp, microseconds + const sync::steady_clock::duration m_delay = sync::milliseconds_from(200); }; // Check the available size of the receiver buffer. @@ -137,6 +172,19 @@ TEST_F(CRcvBufferReadMsg, Create) EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); } +// Check that destroying the buffer also frees memory units. +TEST_F(CRcvBufferReadMsg, Destroy) +{ + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + // Add a number of units (packets) to the buffer + // equal to the buffer size in packets + for (int i = 0; i < getAvailBufferSize(); ++i) + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0); + + m_rcv_buffer.reset(); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + // Fill the buffer full, and check adding more data results in an error. TEST_F(CRcvBufferReadMsg, FullBuffer) { @@ -163,9 +211,11 @@ TEST_F(CRcvBufferReadMsg, FullBuffer) EXPECT_TRUE(size_t(res) == m_payload_sz); EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); } + + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } -// BUG in the new RCV buffer!!! +// BUG in the old RCV buffer!!! // In this test case a packet is added to receiver buffer with offset 1, // thus leaving offset 0 with an empty pointer. // The buffer says it is not empty, and the data is available @@ -187,42 +237,112 @@ TEST_F(CRcvBufferReadMsg, OnePacketGap) EXPECT_EQ(res, 0); // BUG. Acknowledging an empty position must not result in a read-readiness. + // TODO: Actually we should not acknowledge, but must drop instead. ackPackets(1); - // Wrong behavior (BUG) +#if ENABLE_NEW_RCVBUFFER // Expected behavior + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); + + const auto next_packet = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(next_packet.seqno, m_init_seqno + 1); +#else // Wrong behavior (BUG) EXPECT_TRUE(hasAvailablePackets()); EXPECT_TRUE(rcv_buffer.isRcvDataReady()); +#endif EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 2); +#if ENABLE_NEW_RCVBUFFER + // The new buffer will return 0 as reading is not available. + res = rcv_buffer.readBuffer(buff.data(), buff.size()); + EXPECT_EQ(res, 0); +#else cerr << "Expecting IPE from readBuffer(..): \n"; res = rcv_buffer.readBuffer(buff.data(), buff.size()); EXPECT_EQ(res, -1); +#endif res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, 0); + +#if ENABLE_NEW_RCVBUFFER + // Add a missing packet (can't add before an acknowledged position in the old buffer). + EXPECT_EQ(addMessage(1, m_init_seqno), 0); + + for (int pktno = 0; pktno < 2; ++pktno) + { + const size_t msg_bytelen = m_payload_sz; + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); + EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), msg_bytelen, CSeqNo::incseq(m_init_seqno, pktno))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#endif + + // Further read is not possible + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); +} + +/// One packet is added to the buffer after 1-packet gap. Should be read only after ACK. +/// 1. insert (1) +/// | +/// +---+---+ ---+---+---+---+ +---+ +/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+---+ ---+---+---+---+ +---+ +/// 2. drop (0) +/// 2. read (1) +/// +TEST_F(CRcvBufferReadMsg, OnePacketGapDrop) +{ + // Add one packet message to to the buffer + // with a gap of one packet. + EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0); + auto& rcv_buffer = *m_rcv_buffer.get(); + EXPECT_FALSE(hasAvailablePackets()); + EXPECT_FALSE(rcv_buffer.isRcvDataReady()); +#if ENABLE_NEW_RCVBUFFER + rcv_buffer.dropUpTo(CSeqNo::incseq(m_init_seqno)); +#else + rcv_buffer.dropData(1); +#endif + + EXPECT_TRUE(hasAvailablePackets()); + EXPECT_TRUE(rcv_buffer.isRcvDataReady()); + array buff; + EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, CSeqNo::incseq(m_init_seqno))); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Add one packet to the buffer and read it once it is acknowledged. // Confirm the data read is valid. +// Don't allow to add packet with the same sequence number. TEST_F(CRcvBufferReadMsg, OnePacket) { const size_t msg_pkts = 1; // Adding one message without acknowledging - addMessage(msg_pkts, m_init_seqno, false); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0); + // Adding a packet into the same position must return an error. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -1); const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; + // The new receiver buffer allows reading without ACK. +#if !ENABLE_NEW_RCVBUFFER EXPECT_FALSE(hasAvailablePackets()); + const int res1 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res1, 0); // Full ACK ackPackets(msg_pkts); +#endif EXPECT_TRUE(hasAvailablePackets()); const int res2 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res2, msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Add ten packets to the buffer, acknowledge and read some of them. @@ -243,7 +363,12 @@ TEST_F(CRcvBufferReadMsg, AddData) // The value is reported by SRT receiver like this: // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); +#if ENABLE_NEW_RCVBUFFER + // The new receiver buffer does not need ACK to allow reading. + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(hasAvailablePackets()); +#endif // Now acknowledge two packets const int ack_pkts = 2; @@ -261,11 +386,24 @@ TEST_F(CRcvBufferReadMsg, AddData) } // Add packet to the position of oackets already read. - // Can't check, as negative offset is an error not handled by the receiver buffer. - //EXPECT_EQ(addPacket(m_init_seqno), -1); + // Can't check the old buffer, as it does not handle a negative offset. +#if ENABLE_NEW_RCVBUFFER + EXPECT_EQ(addPacket(m_init_seqno), -2); +#endif // Add packet to a non-empty position. EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); + + const int num_pkts_left = num_pkts - ack_pkts; + ackPackets(num_pkts_left); + for (int i = 0; i < num_pkts_left; ++i) + { + const int res = readMessage(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Check reading the whole message (consisting of several packets) from the buffer. @@ -291,6 +429,7 @@ TEST_F(CRcvBufferReadMsg, MsgAcked) const ptrdiff_t offset = i * m_payload_sz; EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // Check reading the whole message (consisting of several packets) into @@ -320,6 +459,7 @@ TEST_F(CRcvBufferReadMsg, SmallReadBuffer) EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // BUG!!! @@ -336,6 +476,19 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) // Nothing to read (0 for zero bytes read). const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; +#if ENABLE_NEW_RCVBUFFER + // The new receiver buffer does not care about ACK. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); @@ -344,14 +497,24 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) // ACK half of the message and check read-readiness. ackPackets(2); // FIXME: Sadly RCV buffer says the data is ready to be read. - // EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); - // EXPECT_FALSE(hasAvailablePackets()); EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); EXPECT_TRUE(hasAvailablePackets()); // Actually must be nothing to read (can't read half a message). const int res2 = readMessage(buff.data(), buff.size()); EXPECT_EQ(res2, 0); + + // ACK the remaining half of the message and check read-readiness. + ackPackets(2); + const int res3 = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res3, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } +#endif + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } // BUG!!! @@ -363,12 +526,39 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, m_init_seqno, true); +#if ENABLE_NEW_RCVBUFFER + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); +#endif const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); + +#if ENABLE_NEW_RCVBUFFER + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#else + ackPackets(msg_pkts); + // The old buffer still does not free units. + EXPECT_NE(m_unit_queue->size(), m_unit_queue->capacity()); + // BUG: wrong read-ready state. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + // Nothing read, but empty units are freed. + EXPECT_EQ(readMessage(buff.data(), buff.size()), 0); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +#endif } // Adding a message with the out-of-order flag set. @@ -379,8 +569,13 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) // Adding one message with the Out-Of-Order flag set, but without acknowledging. addMessage(msg_pkts, CSeqNo::incseq(m_init_seqno, 1), true); +#if ENABLE_NEW_RCVBUFFER + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#else EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); +#endif const size_t msg_bytelen = msg_pkts * m_payload_sz; array buff; const int res = readMessage(buff.data(), buff.size()); @@ -407,10 +602,15 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) EXPECT_TRUE(res3 == m_payload_sz); EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, m_init_seqno)); - // Only "passack" packets remain in the buffer. + // Only "passack" or EntryState_Read packets remain in the buffer. // They are falsely signalled as read-ready. +#if ENABLE_NEW_RCVBUFFER + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); +#else EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); // BUG: nothing to read. EXPECT_TRUE(hasAvailablePackets()); // BUG: nothing to read. +#endif // Adding a packet right after the EntryState_Read packets. const int seqno = CSeqNo::incseq(m_init_seqno, msg_pkts + 1); @@ -422,4 +622,216 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); EXPECT_FALSE(hasAvailablePackets()); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +// One message (4 packets) are added to the buffer. +// Check if reading is only possible once the whole message is present in the buffer. +TEST_F(CRcvBufferReadMsg, LongMsgReadReady) +{ + const size_t msg_pkts = 4; + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + for (size_t i = 0; i < msg_pkts; ++i) + { + // int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0) + const bool pb_first = (i == 0); + const bool pb_last = (i == (msg_pkts - 1)); + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), pb_first, pb_last), 0); + ackPackets(1); + if (!pb_last) + { +#if ENABLE_NEW_RCVBUFFER + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + EXPECT_FALSE(hasAvailablePackets()); +#else + // BUG: The old buffer returns true (read-readiness). + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); +#endif + EXPECT_EQ(readMessage(buff.data(), buff.size()), 0); + } + } + + // Read the whole message. + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_TRUE(hasAvailablePackets()); + + const int res = readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + const ptrdiff_t offset = i * m_payload_sz; + EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +#if ENABLE_NEW_RCVBUFFER +// One message (4 packets) is added to the buffer. Can be read out of order. +// Reading should be possible even before the missing packet is dropped. +TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) +{ + const size_t msg_pkts = 4; + // 1. Add one message (4 packets) without acknowledging + const int msg_seqno = m_init_seqno + 1; // seqno of the first packet in the message + EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), 0); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + + // 2. Read full message after gap. + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + int res = m_rcv_buffer->readMessage(buff.data(), buff.size()); + EXPECT_EQ(res, msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, msg_seqno + i)); + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + + // Can't add to the same message + EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), -1); + + const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(pkt_info.seqno, -1); // Nothing to read + EXPECT_TRUE(srt::sync::is_zero(pkt_info.tsbpd_time)); + + // Drop missing packet + m_rcv_buffer->dropUpTo(msg_seqno); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); + // All memory units are expected to be freed. + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +// One message (4 packets) is added to the buffer after a message with "in order" flag. +// Read in order +TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) +{ + const size_t msg_pkts = 4; + // 1. Add one packet with inOrder=true and one message (4 packets) with inOrder=false + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + 2 * msg_pkts, true), 0); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + msg_pkts, true), 0); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + + // 2. Read messages in order + const size_t msg_bytelen = msg_pkts * m_payload_sz; + std::array buff; + for (int msg_i = 0; msg_i < 3; ++msg_i) + { + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); + EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), msg_bytelen); + for (size_t i = 0; i < msg_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, m_init_seqno + msg_i * msg_pkts + i)); + } + } + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady()); } + +/// One packet is added to the buffer. Can be read on TSBPD-readiness. +/// +/// 1. insert +/// | +/// +---+ ---+---+---+---+---+ +---+ +/// | 1 | 0 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+ ---+---+---+---+---+ +---+ +/// | +/// 2. read +/// +TEST_F(CRcvBufferReadMsg, OnePacketTSBPD) +{ + const size_t msg_pkts = 1; + + m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay); + + const int packet_ts = 0; + // Adding one message. Note that all packets have the out of order flag + // set to false by default in TSBPD mode, but this flag is ignored. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), 0); + + const size_t msg_bytelen = msg_pkts * m_payload_sz; + array buff; + + // Confirm adding to the same location returns an error. + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), -1); + + // There is one packet in the buffer, but not ready to read after delay/2 + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + (m_delay / 2))); + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay - sync::microseconds_from(1))); + // There is one packet in the buffer ready to read after delay + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay + sync::microseconds_from(1))); + + // Read out the first message + const int read_len = m_rcv_buffer->readMessage(buff.data(), buff.size()); + EXPECT_EQ(read_len, msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), read_len, m_init_seqno)); + + // Check the state after a packet was read + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); + EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -2); + + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay)); +} + +/// TSBPD = ON, a ready to play packet is preceeded by a missing packet. +/// The read-rediness must be signalled, and a packet must be read after the missing +/// one is dropped. +/// The TSBPD delay is set to 200 ms. This means, that the packet can be played +/// not earlier than after 200200 microseconds from the peer start time. +/// The peer start time is set to 100000 us. +/// +/// +/// || +/// | / +/// | / +/// | | +/// +---+---+---+---+---+---+ +---+ +/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[] +/// +---+---+---+---+---+---+ +---+ +/// | | +/// | \__last pkt received +/// | +/// \___ m_iStartPos: first message to read +/// \___ m_iLastAckPos: last ack sent +/// +/// m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped +/// +TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) +{ + m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay); + // Add a solo packet to position m_init_seqno + 1 with timestamp 200 us + const int seqno = m_init_seqno + 1; + const int32_t pkt_ts = 200; + EXPECT_EQ(addMessage(1, seqno, false, pkt_ts), 0); + + const auto readready_timestamp = m_tsbpd_base + sync::microseconds_from(pkt_ts) + m_delay; + // Check that getFirstValidPacketInfo() returns first valid packet. + const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo(); + EXPECT_EQ(pkt_info.tsbpd_time, readready_timestamp); + EXPECT_EQ(pkt_info.seqno, seqno); + EXPECT_TRUE(pkt_info.seq_gap); + + // The packet can't be read because there is a missing packet preceeding. + EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(readready_timestamp)); + + const int seq_gap_len = CSeqNo::seqoff(m_rcv_buffer->getStartSeqNo(), pkt_info.seqno); + EXPECT_GT(seq_gap_len, 0); + if (seq_gap_len > 0) + { + m_rcv_buffer->dropUpTo(pkt_info.seqno); + } + + EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(readready_timestamp)); + + const size_t msg_bytelen = m_payload_sz; + array buff; + EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + +#endif // ENABEL_NEW_RCVBUFFER diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 8fb36be53..4644e3267 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -718,7 +718,7 @@ class TestEPoll: public testing::Test ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], acpsock); // read event is for bind socket + ASSERT_EQ(read[0], acpsock); // read event is for bind socket } char buffer[1316]; From f11b026d8efeb25195869bd1f77d757979a17150 Mon Sep 17 00:00:00 2001 From: cg82616424 Date: Tue, 26 Oct 2021 16:17:41 +0800 Subject: [PATCH 194/683] [core] fix listener's cookie check (#2176). If a cookie from the caller HS conclusion response does not match the current listener's cookie, check the one that would have been generated a minute ago by the listener. --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 67c2e6b08..d0849c6b8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10610,7 +10610,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co clientport, sizeof(clientport), NI_NUMERICHOST | NI_NUMERICSERV); - int64_t timestamp = (count_microseconds(steady_clock::now() - m_stats.tsStartTime) / 60000000) + distractor - + int64_t timestamp = (count_microseconds(steady_clock::now() - m_stats.tsStartTime) / 60000000) + distractor + correction; // secret changes every one minute stringstream cookiestr; cookiestr << clienthost << ":" << clientport << ":" << timestamp; From d9b7988347fdbd4c9e1ae7fdc3b0bb3080aa8ae1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 27 Oct 2021 02:42:05 -0500 Subject: [PATCH 195/683] [build] Add ShowProjectConfig CMake Module. (#2161) New build option ENABLE_SHOW_PROJECT_CONFIG. --- CMakeLists.txt | 6 + docs/build/build-options.md | 6 + scripts/ShowProjectConfig.cmake | 190 ++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 scripts/ShowProjectConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 77286fb7a..e48798713 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ option(USE_OPENSSL_PC "Use pkg-config to find OpenSSL libraries" ON) option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potentially higher CPU load" OFF) option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) +option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF) option(ENABLE_NEW_RCVBUFFER "Enable new receiver buffer implementation" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -1340,3 +1341,8 @@ if (DEFINED SRT_EXTRA_APPS_INC) # No extra variables expected. Just use the variables # already provided and define additional targets. endif() + +if (ENABLE_SHOW_PROJECT_CONFIG) + include(ShowProjectConfig) + ShowProjectConfig() +endif() diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 3afce1cf3..cdac4c068 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -52,6 +52,12 @@ All options below are presented using the `configure` convention. They can all be used in `cmake` with the appropriate format changes. +**`--enable-show-project-config`** (default:OFF) + +When ON, the project configuration is displayed at the end of the CMake Configuration +Step. + + **`--cygwin-use-posix`** (default:OFF) When ON, compile on Cygwin using POSIX API (otherwise it will use MinGW environment). diff --git a/scripts/ShowProjectConfig.cmake b/scripts/ShowProjectConfig.cmake new file mode 100644 index 000000000..dc57876c9 --- /dev/null +++ b/scripts/ShowProjectConfig.cmake @@ -0,0 +1,190 @@ +# +# SRT - Secure, Reliable, Transport Copyright (c) 2021 Haivision Systems Inc. +# +# This Source Code Form is subject to the terms of the Mozilla Public License, +# v. 2.0. If a copy of the MPL was not distributed with this file, You can +# obtain one at http://mozilla.org/MPL/2.0/. +# + +function(ShowProjectConfig) + + set(__ssl_configuration) + if (SSL_FOUND OR SSL_LIBRARIES) + set(__ssl_configuration + " SSL Configuration: + SSL_FOUND=${SSL_FOUND} + SSL_INCLUDE_DIRS=${SSL_INCLUDE_DIRS} + SSL_LIBRARIES=${SSL_LIBRARIES} + SSL_VERSION=${SSL_VERSION}\n") + endif() + + set(static_property_link_libraries) + if (srt_libspec_static) + get_target_property( + static_property_link_libraries + ${TARGET_srt}_static + LINK_LIBRARIES) + endif() + set(shared_property_link_libraries) + if (srt_libspec_shared) + get_target_property( + shared_property_link_libraries + ${TARGET_srt}_shared + LINK_LIBRARIES) + endif() + + # See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13 + set(__more_tc1_config) + if (CMAKE_CROSSCOMPILING) + set(__more_tc1_config + " CMAKE_SYSROOT: ${CMAKE_SYSROOT}\n") + endif() + + # See https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#id13 + set(__more_tc2_config) + if (APPLE) + set(__more_tc2_config + " CMAKE_INSTALL_NAME_TOOL: ${CMAKE_INSTALL_NAME_TOOL} + CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} + CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES} + CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET} + CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}\n") + elseif (ANDROID) + set(__more_tc2_config + " CMAKE_ANDROID_NDK: ${CMAKE_ANDROID_NDK} + CMAKE_ANDROID_STANDALONE_TOOLCHAIN: ${CMAKE_ANDROID_STANDALONE_TOOLCHAIN} + CMAKE_ANDROID_API: ${CMAKE_ANDROID_API} + CMAKE_ANDROID_ARCH_ABI: ${CMAKE_ANDROID_ARCH_ABI} + CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION: ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION} + CMAKE_ANDROID_STL_TYPE: ${CMAKE_ANDROID_STL_TYPE}\n") + endif() + + message(STATUS + "\n" + "========================================================================\n" + "= Project Configuration:\n" + "========================================================================\n" + " SRT Version:\n" + " SRT_VERSION: ${SRT_VERSION}\n" + " SRT_VERSION_BUILD: ${SRT_VERSION_BUILD}\n" + " CMake Configuration:\n" + " CMAKE_VERSION: ${CMAKE_VERSION}\n" + " CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}\n" + " CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" + " Target Configuration:\n" + " CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}\n" + " CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}\n" + " CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}\n" + " CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}\n" + " DARWIN: ${DARWIN}\n" + " LINUX: ${LINUX}\n" + " BSD: ${BSD}\n" + " MICROSOFT: ${MICROSOFT}\n" + " GNU: ${GNU}\n" + " ANDROID: ${ANDROID}\n" + " SUNOS: ${SUNOS}\n" + " POSIX: ${POSIX}\n" + " SYMLINKABLE: ${SYMLINKABLE}\n" + " APPLE: ${APPLE}\n" + " UNIX: ${UNIX}\n" + " WIN32: ${WIN32}\n" + " MINGW: ${MINGW}\n" + " CYGWIN: ${CYGWIN}\n" + " CYGWIN_USE_POSIX: ${CYGWIN_USE_POSIX}\n" + " Toolchain Configuration:\n" + " CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}\n" + " CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}\n" + "${__more_tc1_config}" + " CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}\n" + " CMAKE_C_COMPILER_VERSION: ${CMAKE_C_COMPILER_VERSION}\n" + " CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}\n" + " CMAKE_C_FLAGS: '${CMAKE_C_FLAGS}'\n" + " CMAKE_C_COMPILE_FEATURES: ${CMAKE_C_COMPILE_FEATURES}\n" + " CMAKE_C_STANDARD: ${CMAKE_CXX_STANDARD}\n" + " CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}\n" + " CMAKE_CXX_COMPILER_VERSION: ${CMAKE_CXX_COMPILER_VERSION}\n" + " CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}\n" + " CMAKE_CXX_FLAGS: '${CMAKE_CXX_FLAGS}'\n" + " CMAKE_CXX_COMPILE_FEATURES: ${CMAKE_CXX_COMPILE_FEATURES}\n" + " CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}\n" + " CMAKE_LINKER: ${CMAKE_LINKER}\n" + #" CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}\n" + #" CMAKE_EXE_LINKER_FLAGS_INIT: ${CMAKE_EXE_LINKER_FLAGS_INIT}\n" + #" CMAKE_MODULE_LINKER_FLAGS: ${CMAKE_MODULE_LINKER_FLAGS}\n" + #" CMAKE_MODULE_LINKER_FLAGS_INIT: ${CMAKE_MODULE_LINKER_FLAGS_INIT}\n" + #" CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}\n" + #" CMAKE_SHARED_LINKER_FLAGS_INIT: ${CMAKE_SHARED_LINKER_FLAGS_INIT}\n" + #" CMAKE_STATIC_LINKER_FLAGS: ${CMAKE_STATIC_LINKER_FLAGS}\n" + #" CMAKE_STATIC_LINKER_FLAGS_INIT: ${CMAKE_STATIC_LINKER_FLAGS_INIT}\n" + " CMAKE_NM: ${CMAKE_NM}\n" + " CMAKE_AR: ${CMAKE_AR}\n" + " CMAKE_RANLIB: ${CMAKE_RANLIB}\n" + "${__more_tc2_config}" + " HAVE_COMPILER_GNU_COMPAT: ${HAVE_COMPILER_GNU_COMPAT}\n" + " CMAKE_THREAD_LIBS: ${CMAKE_THREAD_LIBS}\n" + " CMAKE_THREAD_LIBS_INIT: ${CMAKE_THREAD_LIBS_INIT}\n" + " ENABLE_THREAD_CHECK: ${ENABLE_THREAD_CHECK}\n" + " USE_CXX_STD_APP: ${USE_CXX_STD_APP}\n" + " USE_CXX_STD_LIB: ${USE_CXX_STD_LIB}\n" + " STDCXX: ${STDCXX}\n" + " USE_CXX_STD: ${USE_CXX_STD}\n" + " HAVE_CLOCK_GETTIME_IN: ${HAVE_CLOCK_GETTIME_IN}\n" + " HAVE_CLOCK_GETTIME_LIBRT: ${HAVE_CLOCK_GETTIME_LIBRT}\n" + " HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H}\n" + " HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H: ${HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H}\n" + " HAVE_PTHREAD_GETNAME_NP: ${HAVE_PTHREAD_GETNAME_NP}\n" + " HAVE_PTHREAD_SETNAME_NP: ${HAVE_PTHREAD_SETNAME_NP}\n" + " HAVE_LIBATOMIC: ${HAVE_LIBATOMIC}\n" + " HAVE_LIBATOMIC_COMPILES: ${HAVE_LIBATOMIC_COMPILES}\n" + " HAVE_LIBATOMIC_COMPILES_STATIC: ${HAVE_LIBATOMIC_COMPILES_STATIC}\n" + " HAVE_GCCATOMIC_INTRINSICS: ${HAVE_GCCATOMIC_INTRINSICS}\n" + " HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC: ${HAVE_GCCATOMIC_INTRINSICS_REQUIRES_LIBATOMIC}\n" + " HAVE_CXX_ATOMIC: ${HAVE_CXX_ATOMIC}\n" + " HAVE_CXX_ATOMIC_STATIC: ${HAVE_CXX_ATOMIC_STATIC}\n" + " Project Configuration:\n" + " ENABLE_DEBUG: ${ENABLE_DEBUG}\n" + " ENABLE_CXX11: ${ENABLE_CXX11}\n" + " ENABLE_APPS: ${ENABLE_APPS}\n" + " ENABLE_EXAMPLES: ${ENABLE_EXAMPLES}\n" + " ENABLE_EXPERIMENTAL_BONDING: ${ENABLE_EXPERIMENTAL_BONDING}\n" + " ENABLE_TESTING: ${ENABLE_TESTING}\n" + " ENABLE_PROFILE: ${ENABLE_PROFILE}\n" + " ENABLE_LOGGING: ${ENABLE_LOGGING}\n" + " ENABLE_HEAVY_LOGGING: ${ENABLE_HEAVY_LOGGING}\n" + " ENABLE_HAICRYPT_LOGGING: ${ENABLE_HAICRYPT_LOGGING}\n" + " ENABLE_SHARED: ${ENABLE_SHARED}\n" + " ENABLE_STATIC: ${ENABLE_STATIC}\n" + " ENABLE_RELATIVE_LIBPATH: ${ENABLE_RELATIVE_LIBPATH}\n" + " ENABLE_GETNAMEINFO: ${ENABLE_GETNAMEINFO}\n" + " ENABLE_UNITTESTS: ${ENABLE_UNITTESTS}\n" + " ENABLE_ENCRYPTION: ${ENABLE_ENCRYPTION}\n" + " ENABLE_CXX_DEPS: ${ENABLE_CXX_DEPS}\n" + " USE_STATIC_LIBSTDCXX: ${USE_STATIC_LIBSTDCXX}\n" + " ENABLE_INET_PTON: ${ENABLE_INET_PTON}\n" + " ENABLE_CODE_COVERAGE: ${ENABLE_CODE_COVERAGE}\n" + " ENABLE_MONOTONIC_CLOCK: ${ENABLE_MONOTONIC_CLOCK}\n" + " ENABLE_STDCXX_SYNC: ${ENABLE_STDCXX_SYNC}\n" + " USE_OPENSSL_PC: ${USE_OPENSSL_PC}\n" + " USE_BUSY_WAITING: ${USE_BUSY_WAITING}\n" + " USE_GNUSTL: ${USE_GNUSTL}\n" + " ENABLE_SOCK_CLOEXEC: ${ENABLE_SOCK_CLOEXEC}\n" + " ENABLE_SHOW_PROJECT_CONFIG: ${ENABLE_SHOW_PROJECT_CONFIG}\n" + " ENABLE_CLANG_TSA: ${ENABLE_CLANG_TSA}\n" + " ATOMIC_USE_SRT_SYNC_MUTEX: ${ATOMIC_USE_SRT_SYNC_MUTEX}\n" + " Constructed Configuration:\n" + " DISABLE_CXX11: ${DISABLE_CXX11}\n" + " HAVE_INET_PTON: ${HAVE_INET_PTON}\n" + " PTHREAD_LIBRARY: ${PTHREAD_LIBRARY}\n" + " USE_ENCLIB: ${USE_ENCLIB}\n" + "${__ssl_configuration}" + " TARGET_srt: ${TARGET_srt}\n" + " srt_libspec_static: ${srt_libspec_static}\n" + " srt_libspec_shared: ${srt_libspec_shared}\n" + " SRT_LIBS_PRIVATE: ${SRT_LIBS_PRIVATE}\n" + " Target Link Libraries:\n" + " Static: ${static_property_link_libraries}\n" + " Shared: ${shared_property_link_libraries}\n" + "========================================================================\n" + ) + +endfunction(ShowProjectConfig) From 3c3824fe28f655c64329a9bb344cb5c59002e7b6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:20:59 +0100 Subject: [PATCH 196/683] [core] Removed unused SRT_DEBUG_TSBPD_DRIFT --- CMakeLists.txt | 2 +- srtcore/buffer.cpp | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e48798713..7d56c1200 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ endforeach() # SRT_ENABLE_ECN 1 /* Early Congestion Notification (for source bitrate control) */ # SRT_DEBUG_TSBPD_OUTJITTER 1 /* Packet Delivery histogram */ -# SRT_DEBUG_TSBPD_DRIFT 1 /* Debug Encoder-Decoder Drift) */ +# SRT_DEBUG_TRACE_DRIFT 1 /* Create a trace log for Encoder-Decoder Clock Drift */ # SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */ # SRT_DEBUG_TLPKTDROP_DROPSEQ 1 # SRT_DEBUG_SNDQ_HIGHRATE 1 diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index e861654eb..c43cc4c59 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -816,14 +816,6 @@ void CSndBuffer::increase() * m_iMaxPos: none? (modified on add and ack */ -// XXX Init values moved to in-class. -// const uint32_t CRcvBuffer::TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec) -// const int CRcvBuffer::TSBPD_DRIFT_MAX_VALUE = 5000; // usec -// const int CRcvBuffer::TSBPD_DRIFT_MAX_SAMPLES = 1000; // ACK-ACK packets -#ifdef SRT_DEBUG_TSBPD_DRIFT -// const int CRcvBuffer::TSBPD_DRIFT_PRT_SAMPLES = 200; // ACK-ACK packets -#endif - CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) : m_pUnit(NULL) , m_iSize(bufsize_pkts) @@ -842,11 +834,6 @@ CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize_pkts) for (int i = 0; i < m_iSize; ++i) m_pUnit[i] = NULL; -#ifdef SRT_DEBUG_TSBPD_DRIFT - memset(m_TsbPdDriftHisto100us, 0, sizeof(m_TsbPdDriftHisto100us)); - memset(m_TsbPdDriftHisto1ms, 0, sizeof(m_TsbPdDriftHisto1ms)); -#endif - setupMutex(m_BytesCountLock, "BytesCount"); } From ec571a034990499df0cae406873084a58dbe8cb1 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:23:30 +0100 Subject: [PATCH 197/683] [core] Fixed new RCV buffer in stream mode (reading fractional packets) --- srtcore/buffer_rcv.cpp | 54 ++++++++++++------------------------------ srtcore/buffer_rcv.h | 2 +- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 12e935057..0ef1d75de 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -369,9 +369,10 @@ namespace { /// @brief Writes bytes to file stream. /// @param data pointer to data to write. /// @param len the number of bytes to write + /// @param dst_offset ignored /// @param arg a void pointer to the fstream to write to. /// @return true on success, false on failure - bool writeBytesToFile(char* data, int len, void* arg) + bool writeBytesToFile(char* data, int len, int dst_offset SRT_ATR_UNUSED, void* arg) { fstream* pofs = reinterpret_cast(arg); pofs->write(data, len); @@ -381,11 +382,12 @@ namespace { /// @brief Copies bytes to the destination buffer. /// @param data pointer to data to copy. /// @param len the number of bytes to copy + /// @param dst_offset offset in destination buffer /// @param arg A pointer to the destination buffer /// @return true on success, false on failure - bool copyBytesToBuf(char* data, int len, void* arg) + bool copyBytesToBuf(char* data, int len, int dst_offset, void* arg) { - char* dst = reinterpret_cast(arg); + char* dst = reinterpret_cast(arg) + dst_offset; memcpy(dst, data, len); return true; } @@ -427,7 +429,7 @@ int CRcvBufferNew::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) const int remain_pktlen = pktlen - m_iNotch; const int unitsize = std::min(remain_pktlen, rs); - if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, arg)) + if (!funcCopyToDst(pkt.m_pcData + m_iNotch, unitsize, len - rs, arg)) break; if (rs >= remain_pktlen) @@ -622,45 +624,23 @@ void CRcvBufferNew::updateNonreadPos() if (m_iMaxPosInc == 0) return; - // const PacketBoundary boundary = packet.getMsgBoundary(); - - //// The simplest case is when inserting a sequential PB_SOLO packet. - // if (boundary == PB_SOLO && (m_iFirstNonreadPos + 1) % m_szSize == pos) - //{ - // m_iFirstNonreadPos = pos; - // return; - //} const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; - while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST)) + while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - // bool good = true; - - // look ahead for the whole message - - // We expect to see either of: - // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - // [PB_SOLO] - // but not: - // [PB_FIRST] NULL ... - // [PB_FIRST] FREE/PASSACK/DROPPED... - // If the message didn't look as expected, interrupt this. - - // This begins with a message starting at m_iStartPos - // up to end_pos (excluding) OR until the PB_LAST message is found. - // If any of the units on this way isn't good, this OUTER loop - // will be interrupted. - for (int i = pos; i != end_pos; i = (i + 1) % m_szSize) + if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + break; + + for (int i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { - // good = false; break; } - // Likewise, boundary() & PB_LAST will be satisfied for last OR solo. - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + // Check PB_LAST only in message mode. + if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -672,11 +652,6 @@ void CRcvBufferNew::updateNonreadPos() pos = m_iFirstNonreadPos; } - - // 1. If there is a gap between this packet and m_iLastReadablePos - // then no sense to update m_iLastReadablePos. - - // 2. The simplest case is when this is the first sequential packet } int CRcvBufferNew::findLastMessagePkt() @@ -929,9 +904,10 @@ string CRcvBufferNew::strFullnessState(int iFirstUnackSeqNo, const time_point& t { ss << ":n/a ms"; } + ss << ". "; } - ss << ". " SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; + ss << SRT_SYNC_CLOCK_STR " drift " << getDrift() / 1000 << " ms."; return ss.str(); } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index dfc63d3f6..490d281d9 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -233,7 +233,7 @@ class CRcvBufferNew int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; - typedef bool copy_to_dst_f(char* data, int len, void* arg); + typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); /// Read acknowledged data directly into file. /// @param [in] ofs C++ file stream. From 01ef57a9a9e8c0a1e6b6615bdf7849959a99a482 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 5 Nov 2021 10:59:57 +0100 Subject: [PATCH 198/683] [tests] Added unit tests for RCV buffer in stream mode --- test/test_buffer.cpp | 116 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 896303d59..2ba636525 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -11,12 +11,13 @@ class CRcvBufferReadMsg : public ::testing::Test { protected: - CRcvBufferReadMsg() + CRcvBufferReadMsg(bool message_api = true) + : m_use_message_api(message_api) { // initialization code here } - ~CRcvBufferReadMsg() + virtual ~CRcvBufferReadMsg() { // cleanup any pending stuff, but no exceptions allowed } @@ -31,7 +32,7 @@ class CRcvBufferReadMsg m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET); #if ENABLE_NEW_RCVBUFFER - const bool enable_msg_api = true; + const bool enable_msg_api = m_use_message_api; const bool enable_peer_rexmit = true; m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); #else @@ -161,6 +162,7 @@ class CRcvBufferReadMsg const int m_init_seqno = 1000; int m_first_unack_seqno = m_init_seqno; static const size_t m_payload_sz = 1456; + const bool m_use_message_api; const sync::steady_clock::time_point m_tsbpd_base = sync::steady_clock::now(); // now() - HS.timestamp, microseconds const sync::steady_clock::duration m_delay = sync::milliseconds_from(200); @@ -834,4 +836,112 @@ TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } + +class CRcvBufferReadStream + : public CRcvBufferReadMsg +{ +protected: + CRcvBufferReadStream() + : CRcvBufferReadMsg(false) + {} + + virtual ~CRcvBufferReadStream() { } +}; + + +// Add ten packets to the buffer in stream mode, read some of them. +// Try to add packets to occupied positions. +TEST_F(CRcvBufferReadStream, ReadSinglePackets) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0); + } + + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + // Now acknowledge two packets + const int ack_pkts = 2; + ackPackets(2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts); + EXPECT_TRUE(hasAvailablePackets()); + + std::array buff; + for (int i = 0; i < ack_pkts; ++i) + { + const size_t res = m_rcv_buffer->readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i))); + } + + // Add packet to the position of oackets already read. + // Can't check the old buffer, as it does not handle a negative offset. + EXPECT_EQ(addPacket(m_init_seqno), -2); + + // Add packet to a non-empty position. + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1); + + const int num_pkts_left = num_pkts - ack_pkts; + ackPackets(num_pkts_left); + for (int i = 0; i < num_pkts_left; ++i) + { + const int res = m_rcv_buffer->readBuffer(buff.data(), buff.size()); + EXPECT_TRUE(size_t(res) == m_payload_sz); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i); + EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i))); + } + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + + +// Add packets to the buffer in stream mode. Read fractional number of packets +// to confirm a partially read packet stays in the buffer and is read properly afterwards. +TEST_F(CRcvBufferReadStream, ReadFractional) +{ + const int num_pkts = 10; + ASSERT_LT(num_pkts, m_buff_size_pkts); + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0); + } + + // The available buffer size remains the same + // The value is reported by SRT receiver like this: + // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize(); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + array buff; + + const size_t nfull_pkts = 2; + const size_t num_bytes1 = nfull_pkts * m_payload_sz + m_payload_sz / 2; + const int res1 = m_rcv_buffer->readBuffer(buff.data(), num_bytes1); + EXPECT_TRUE(size_t(res1) == num_bytes1); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - nfull_pkts - 1); + EXPECT_TRUE(hasAvailablePackets()); + + const size_t num_bytes2 = m_payload_sz * (num_pkts - nfull_pkts - 1) + m_payload_sz / 2; + + const int res2 = m_rcv_buffer->readBuffer(buff.data() + num_bytes1, buff.size() - num_bytes1); + EXPECT_TRUE(size_t(res2) == num_bytes2); + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts - 1); + EXPECT_FALSE(hasAvailablePackets()); + ackPackets(num_pkts); // Move the reference ACK position. + EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); + + for (int i = 0; i < num_pkts; ++i) + { + EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))) << "i = " << i; + } + + EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); +} + #endif // ENABEL_NEW_RCVBUFFER From e4a1d2b01919809f8a0efb96788261689202ccd7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Nov 2021 16:42:24 +0100 Subject: [PATCH 199/683] [core] Fixed read-ready epoll event in stream (file) mode. Only the new RCV buffer (PR #1964) is affected. --- srtcore/core.cpp | 80 ++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d0849c6b8..9aa2c5eb2 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6275,8 +6275,8 @@ int srt::CUDT::receiveBuffer(char *data, int len) return 0; } HLOGC(arlog.Debug, - log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") - << " SHUTDOWN. Reporting as BROKEN."); + log << (m_config.bMessageAPI ? "MESSAGE" : "STREAM") << " API, " << (m_bShutdown ? "" : "no") + << " SHUTDOWN. Reporting as BROKEN."); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } @@ -6288,31 +6288,29 @@ int srt::CUDT::receiveBuffer(char *data, int len) { throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } - else + + // Kick TsbPd thread to schedule next wakeup (if running) + if (m_config.iRcvTimeOut < 0) { - /* Kick TsbPd thread to schedule next wakeup (if running) */ - if (m_config.iRcvTimeOut < 0) + THREAD_PAUSED(); + while (stillConnected() && !isRcvBufferReady()) { - THREAD_PAUSED(); - while (stillConnected() && !isRcvBufferReady()) - { - // Do not block forever, check connection status each 1 sec. - rcond.wait_for(seconds_from(1)); - } - THREAD_RESUMED(); + // Do not block forever, check connection status each 1 sec. + rcond.wait_for(seconds_from(1)); } - else + THREAD_RESUMED(); + } + else + { + const steady_clock::time_point exptime = + steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); + THREAD_PAUSED(); + while (stillConnected() && !isRcvBufferReady()) { - const steady_clock::time_point exptime = - steady_clock::now() + milliseconds_from(m_config.iRcvTimeOut); - THREAD_PAUSED(); - while (stillConnected() && !isRcvBufferReady()) - { - if (!rcond.wait_until(exptime)) // NOT means "not received a signal" - break; // timeout - } - THREAD_RESUMED(); + if (!rcond.wait_until(exptime)) // NOT means "not received a signal" + break; // timeout } + THREAD_RESUMED(); } } @@ -8066,23 +8064,31 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { UniqueLock rdlock (m_RecvLock); CSync rdcond (m_RecvDataCond, rdlock); - if (m_config.bSynRecving) + +#if ENABLE_NEW_RCVBUFFER + // Locks m_RcvBufferLock, which is unlocked above by InvertedLock un_bufflock. + // Must check read-readiness under m_RecvLock to protect the epoll from concurrent changes in readBuffer() + if (isRcvBufferReady()) +#endif { - // signal a waiting "recv" call if there is any data available - rdcond.signal_locked(rdlock); + if (m_config.bSynRecving) + { + // signal a waiting "recv" call if there is any data available + rdcond.signal_locked(rdlock); + } + // acknowledge any waiting epolls to read + // fix SRT_EPOLL_IN event loss but rcvbuffer still have data: + // 1. user call receive/receivemessage(about line number:6482) + // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false + // 3. but if we do not do some lock work here, will cause some sync problems between threads: + // (1) user thread: call receive/receivemessage + // (2) user thread: read data + // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false + // (4) receive thread: receive data and set SRT_EPOLL_IN to true + // (5) user thread: set SRT_EPOLL_IN to false + // 4. so , m_RecvLock must be used here to protect epoll event + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } - // acknowledge any waiting epolls to read - // fix SRT_EPOLL_IN event loss but rcvbuffer still have data: - // 1. user call receive/receivemessage(about line number:6482) - // 2. after read/receive, if rcvbuffer is empty, will set SRT_EPOLL_IN event to false - // 3. but if we do not do some lock work here, will cause some sync problems between threads: - // (1) user thread: call receive/receivemessage - // (2) user thread: read data - // (3) user thread: no data in rcvbuffer, set SRT_EPOLL_IN event to false - // (4) receive thread: receive data and set SRT_EPOLL_IN to true - // (5) user thread: set SRT_EPOLL_IN to false - // 4. so , m_RecvLock must be used here to protect epoll event - uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, SRT_EPOLL_IN, true); } #if ENABLE_EXPERIMENTAL_BONDING if (m_parent->m_GroupOf) From 5f3cd06c49a99b74fc7877f9b8c3c3ff29f7507a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 12 Nov 2021 17:41:12 +0100 Subject: [PATCH 200/683] [core] Fixed std::runtime_error usage (use C++03 version instead of C++11) (#2184) --- srtcore/strerror_defs.cpp | 2 +- srtcore/utilities.h | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/srtcore/strerror_defs.cpp b/srtcore/strerror_defs.cpp index 7393c1279..1b4c72e4e 100644 --- a/srtcore/strerror_defs.cpp +++ b/srtcore/strerror_defs.cpp @@ -140,7 +140,7 @@ const char* strerror_get_message(size_t major, size_t minor) } const char** array = strerror_array_major[major]; - size_t size = strerror_array_sizes[major]; + const size_t size = strerror_array_sizes[major]; if (minor >= size) { diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 1610505b8..241046ddc 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -44,6 +44,7 @@ written by #include #include #include +#include // -------------- UTILITIES ------------------------ @@ -418,7 +419,8 @@ class FixedArray { public: FixedArray(size_t size) - : m_size(size) + : m_strIndexErr("FixedArray: invalid index") + , m_size(size) , m_entries(new T[size]) { } @@ -432,7 +434,7 @@ class FixedArray const T& operator[](size_t index) const { if (index >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -440,7 +442,7 @@ class FixedArray T& operator[](size_t index) { if (index >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -448,7 +450,7 @@ class FixedArray const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -456,7 +458,7 @@ class FixedArray T& operator[](int index) { if (index < 0 || static_cast(index) >= m_size) - throw std::runtime_error("Invalid index"); + throw std::runtime_error(m_strIndexErr); return m_entries[index]; } @@ -468,8 +470,9 @@ class FixedArray FixedArray& operator=(const FixedArray&); private: - size_t m_size; - T* const m_entries; + const char* m_strIndexErr; + size_t m_size; + T* const m_entries; }; } // namespace srt From 33c8e492a0014a8173487f89205aab82067ca4b2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 25 Oct 2021 15:27:54 +0200 Subject: [PATCH 201/683] [core] checkNeedDrop returns the congestion state --- srtcore/core.cpp | 14 +++++++------- srtcore/core.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9aa2c5eb2..1833b25d8 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6362,10 +6362,10 @@ int srt::CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -void srt::CUDT::checkNeedDrop(bool& w_bCongestion) +bool srt::CUDT::checkNeedDrop() { if (!m_bPeerTLPktDrop) - return; + return false; if (!m_config.bMessageAPI) { @@ -6390,6 +6390,7 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) (2 * COMM_SYN_INTERVAL_US / 1000); } + bool bCongestion = false; if (threshold_ms && timespan_ms > threshold_ms) { // protect packet retransmission @@ -6447,7 +6448,7 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) } #endif } - w_bCongestion = true; + bCongestion = true; leaveCS(m_RecvAckLock); } else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2)) @@ -6455,8 +6456,9 @@ void srt::CUDT::checkNeedDrop(bool& w_bCongestion) HLOGC(aslog.Debug, log << "cong, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms"); - w_bCongestion = true; + bCongestion = true; } + return bCongestion; } int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) @@ -6473,8 +6475,6 @@ int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64 // which is the only case when the m_parent->m_GroupOf is not NULL. int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) { - bool bCongestion = false; - // throw an exception if not connected if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -6563,7 +6563,7 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) // checkNeedDrop(...) may lock m_RecvAckLock // to modify m_pSndBuffer and m_pSndLossList - checkNeedDrop((bCongestion)); + const bool bCongestion = checkNeedDrop(); int minlen = 1; // Minimum sender buffer space required for STREAM API if (m_config.bMessageAPI) diff --git a/srtcore/core.h b/srtcore/core.h index 9f957d040..54c3927e2 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -541,7 +541,7 @@ class CUDT void updateIdleLinkFrom(CUDT* source); - void checkNeedDrop(bool& bCongestion); + bool checkNeedDrop(); /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, From b99e41cc2a2b508eb03c0cfd8828ec6121e28375 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 28 Oct 2021 12:33:48 +0200 Subject: [PATCH 202/683] [core] SND buffer: operate either origin or source time, not both. --- srtcore/buffer.cpp | 57 +++++++++++++++++++--------------------------- srtcore/buffer.h | 8 +++---- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index c43cc4c59..7102d09a8 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -193,21 +193,28 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) HLOGC(bslog.Debug, log << "addBuffer: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << size << " buffers for " << len << " bytes"); + // Retrieve current time before locking the mutex to be closer to packet submission event. + const steady_clock::time_point tnow = steady_clock::now(); - // dynamically increase sender buffer + ScopedLock bufferguard(m_BufLock); + // Dynamically increase sender buffer if there is not enough room. while (size + m_iCount >= m_iSize) { HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (size + m_iCount - m_iSize) << " buffers..."); increase(); } - const steady_clock::time_point time = steady_clock::now(); const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0; - HLOGC(bslog.Debug, log << CONID() << "addBuffer: adding " << size << " packets (" << len << " bytes) to send, msgno=" << (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order"); + // Calculate origin time (same for all blocks of the message). + m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow; + // Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it. + // May also be a subject to conversion error, thus the actual value is signalled back. + w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch()); + // The sequence number passed to this function is the sequence number // that the very first packet from the packet series should get here. // If there's more than one packet, this function must increase it by itself @@ -253,33 +260,21 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) // [PB_FIRST] [PB_LAST] - 2 packets per message // [PB_SOLO] - 1 packet per message - s->m_llSourceTime_us = w_srctime; - s->m_tsOriginTime = time; - s->m_tsRexmitTime = time_point(); s->m_iTTL = ttl; - // Rewrite the actual sending time back into w_srctime - // so that the calling facilities can reuse it - if (!w_srctime) - w_srctime = count_microseconds(s->m_tsOriginTime.time_since_epoch()); - - // XXX unchecked condition: s->m_pNext == NULL. + s->m_tsRexmitTime = time_point(); + s->m_tsOriginTime = m_tsLastOriginTime; + // Should never happen, as the call to increase() should ensure enough buffers. SRT_ASSERT(s->m_pNext); s = s->m_pNext; } m_pLastBlock = s; - enterCS(m_BufLock); m_iCount += size; - m_iBytesCount += len; - m_tsLastOriginTime = time; - updateInputRate(time, size, len); - - updAvgBufSize(time); - - leaveCS(m_BufLock); + updateInputRate(m_tsLastOriginTime, size, len); + updAvgBufSize(m_tsLastOriginTime); // MSGNO_SEQ::mask has a form: 00000011111111... // At least it's known that it's from some index inside til the end (to bit 0). @@ -402,16 +397,6 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) return total; } -steady_clock::time_point CSndBuffer::getSourceTime(const CSndBuffer::Block& block) -{ - if (block.m_llSourceTime_us) - { - return steady_clock::time_point() + microseconds_from(block.m_llSourceTime_us); - } - - return block.m_tsOriginTime; -} - int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) { // No data to read @@ -459,7 +444,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; - w_srctime = getSourceTime(*m_pCurrBlock); + w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); @@ -593,7 +578,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // the packet originally (the other overload of this function) must set these // flags. w_packet.m_iMsgNo = p->m_iMsgNoBitset; - w_srctime = getSourceTime(*p); + w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. // Therefore we are setting the rexmit time. @@ -683,11 +668,17 @@ int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) * Also, if there is only one pkt in buffer, the time difference will be 0. * Therefore, always add 1 ms if not empty. */ - w_timespan = 0 < m_iCount ? count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; + w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0; return m_iCount; } +CSndBuffer::time_point CSndBuffer::getOldestTime() const +{ + SRT_ASSERT(m_pFirstBlock); + return m_pFirstBlock->m_tsOriginTime; +} + int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) { int dpkts = 0; diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 428db748d..9b6259226 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -178,6 +178,8 @@ class CSndBuffer int getAvgBufSize(int& bytes, int& timespan); int getCurrBufSize(int& bytes, int& timespan); + time_point getOldestTime() const; + uint64_t getInRatePeriod() const { return m_InRatePeriod; } /// Retrieve input bitrate in bytes per second @@ -197,9 +199,6 @@ class CSndBuffer void increase(); void setInputRateSmpPeriod(int period); - struct Block; // Defined below - static time_point getSourceTime(const CSndBuffer::Block& block); - private: // Constants static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms @@ -216,9 +215,8 @@ class CSndBuffer int32_t m_iMsgNoBitset; // message number int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // original request time + time_point m_tsOriginTime; // block origin time (either provided from above or equials the time a message was submitted for sending. time_point m_tsRexmitTime; // packet retransmission time - uint64_t m_llSourceTime_us; int m_iTTL; // time to live (milliseconds) Block* m_pNext; // next block From 6ae42c67b85ab09a5c79f4d9173f0c54486b9b32 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 24 Nov 2021 17:49:06 +0800 Subject: [PATCH 203/683] [core] Drop msg by TTL even if hasn't ever been sent (#2068) --- docs/API/API-functions.md | 7 +-- srtcore/buffer.cpp | 107 +++++++++++++++++++++----------------- srtcore/buffer.h | 15 +++--- srtcore/core.cpp | 21 +++++--- 4 files changed, 83 insertions(+), 67 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 58b729fad..e1043b9f4 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -1703,11 +1703,8 @@ called function should work. - `msgttl`: [IN]. In **message** and **live mode** only, specifies the TTL for sending messages (in `[ms]`). Not used for receiving messages. If this value is not negative, it defines the maximum time up to which this message should -stay scheduled for sending for the sake of later retransmission. A message -is always sent for the first time, but the UDP packet carrying it may be -(also partially) lost, and if so, lacking packets will be retransmitted. If -the message is not successfully resent before TTL expires, further retransmission -is given up and the message is discarded. +stay scheduled for sending. If TTL has expired, the message sending and further retransmissions are discarded, even +if it has never been sent so far. - `inorder`: [IN]. In **message mode** only, specifies that sent messages should be extracted by the receiver in the order of sending. This can be meaningful if diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 7102d09a8..d07080981 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -397,57 +397,70 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) return total; } -int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs) +int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc) { - // No data to read - if (m_pCurrBlock == m_pLastBlock) - return 0; + int readlen = 0; + w_seqnoinc = 0; + + while (m_pCurrBlock != m_pLastBlock) + { + // Make the packet REFLECT the data stored in the buffer. + w_packet.m_pcData = m_pCurrBlock->m_pcData; + readlen = m_pCurrBlock->m_iLength; + w_packet.setLength(readlen); + w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + + // XXX This is probably done because the encryption should happen + // just once, and so this sets the encryption flags to both msgno bitset + // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption + // happen at the time when scheduling a new packet to send, but the packet + // must remain in the send buffer until it's ACKed. For the case of rexmit + // the packet will be taken "as is" (that is, already encrypted). + // + // The problem is in the order of things: + // 0. When the application stores the data, some of the flags for PH_MSGNO are set. + // 1. The readData() is called to get the original data sent by the application. + // 2. The data are original and must be encrypted. They WILL BE encrypted, later. + // 3. So far we are in readData() so the encryption flags must be updated NOW because + // later we won't have access to the block's data. + // 4. After exiting from readData(), the packet is being encrypted. It's immediately + // sent, however the data must remain in the sending buffer until they are ACKed. + // 5. In case when rexmission is needed, the second overloaded version of readData + // is being called, and the buffer + PH_MSGNO value is extracted. All interesting + // flags must be present and correct at that time. + // + // The only sensible way to fix this problem is to encrypt the packet not after + // extracting from here, but when the packet is stored into CSndBuffer. The appropriate + // flags for PH_MSGNO will be applied directly there. Then here the value for setting + // PH_MSGNO will be set as is. + + if (kflgs == -1) + { + HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); + readlen = 0; + } + else + { + m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); + } - // Make the packet REFLECT the data stored in the buffer. - w_packet.m_pcData = m_pCurrBlock->m_pcData; - int readlen = m_pCurrBlock->m_iLength; - w_packet.setLength(readlen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; - - // XXX This is probably done because the encryption should happen - // just once, and so this sets the encryption flags to both msgno bitset - // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption - // happen at the time when scheduling a new packet to send, but the packet - // must remain in the send buffer until it's ACKed. For the case of rexmit - // the packet will be taken "as is" (that is, already encrypted). - // - // The problem is in the order of things: - // 0. When the application stores the data, some of the flags for PH_MSGNO are set. - // 1. The readData() is called to get the original data sent by the application. - // 2. The data are original and must be encrypted. They WILL BE encrypted, later. - // 3. So far we are in readData() so the encryption flags must be updated NOW because - // later we won't have access to the block's data. - // 4. After exiting from readData(), the packet is being encrypted. It's immediately - // sent, however the data must remain in the sending buffer until they are ACKed. - // 5. In case when rexmission is needed, the second overloaded version of readData - // is being called, and the buffer + PH_MSGNO value is extracted. All interesting - // flags must be present and correct at that time. - // - // The only sensible way to fix this problem is to encrypt the packet not after - // extracting from here, but when the packet is stored into CSndBuffer. The appropriate - // flags for PH_MSGNO will be applied directly there. Then here the value for setting - // PH_MSGNO will be set as is. + Block* p = m_pCurrBlock; + w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_srctime = m_pCurrBlock->m_tsOriginTime; + m_pCurrBlock = m_pCurrBlock->m_pNext; - if (kflgs == -1) - { - HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); - readlen = 0; - } - else - { - m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs); - } - - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; - w_srctime = m_pCurrBlock->m_tsOriginTime; - m_pCurrBlock = m_pCurrBlock->m_pNext; + if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL)) + { + LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL); + // Skip this packet due to TTL expiry. + readlen = 0; + ++w_seqnoinc; + continue; + } - HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send"); + break; + } return readlen; } diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 9b6259226..6478e30b9 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -142,17 +142,18 @@ class CSndBuffer int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. - /// @param [out] w_packet data packet buffer to fill. - /// @param [out] w_origintime origin time stamp of the message. - /// @param [in] kflags Odd|Even crypto key flag. + /// @param [out] packet the packet to read. + /// @param [out] origintime origin time stamp of the message + /// @param [in] kflags Odd|Even crypto key flag + /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. /// @return Actual length of data read. - int readData(CPacket& w_packet, time_point& w_origintime, int kflgs); + int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); /// Find data position to pack a DATA packet for a retransmission. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] w_packet data packet buffer to fill. - /// @param [out] w_origintime origin time stamp of the message - /// @param [out] w_msglen length of the message + /// @param [out] packet the packet to read. + /// @param [out] origintime origin time stamp of the message + /// @param [out] msglen length of the message /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1833b25d8..b140c6422 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9219,17 +9219,15 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi SRT_ASSERT(msglen >= 1); seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); - HLOGC(qrlog.Debug, log << "IPE: loss-reported packets not found in SndBuf - requesting DROP: " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" - << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); + HLOGC(qrlog.Debug, + log << "loss-reported packets expired in SndBuf - requesting DROP: " + << "msgno=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen + << " SEQ:" << seqpair[0] << " - " << seqpair[1]); sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); - // only one msg drop request is necessary - m_pSndLossList->removeUpTo(seqpair[1]); - // skip all dropped packets + m_pSndLossList->removeUpTo(seqpair[1]); m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); - continue; } else if (payload == 0) @@ -9327,7 +9325,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. kflg = m_pCryptoControl->getSndCryptoFlags(); - payload = m_pSndBuffer->readData((w_packet), (origintime), kflg); + int pktskipseqno = 0; + payload = m_pSndBuffer->readData((w_packet), (origintime), kflg, (pktskipseqno)); + if (pktskipseqno) + { + // Some packets were skipped due to TTL expiry. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); + } + if (payload) { // A CHANGE. The sequence number is currently added to the packet From e5a11795326249c65a761b413c90fd2871bdaab1 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Wed, 1 Dec 2021 03:43:10 -0600 Subject: [PATCH 204/683] [core] Fix UDP RCVBUF and SNDBUF on Solaris (#2162). Check system maximum is not exceeded. --- srtcore/channel.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 85c39ba95..8451e5b66 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -265,7 +265,81 @@ void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr) void srt::CChannel::setUDPSockOpt() { -#if defined(BSD) || TARGET_OS_MAC +#if defined(SUNOS) + { + socklen_t optSize; + // Retrieve starting SND/RCV Buffer sizes. + int startRCVBUF = 0; + optSize = sizeof(startRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)&startRCVBUF, &optSize)) + { + startRCVBUF = -1; + } + int startSNDBUF = 0; + optSize = sizeof(startSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (void*)&startSNDBUF, &optSize)) + { + startSNDBUF = -1; + } + + // SunOS will fail setsockopt() if the requested buffer size exceeds system + // maximum value. + // However, do not reduce the buffer size. + const int maxsize = 64000; + if (0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize)) + { + int currentRCVBUF = 0; + optSize = sizeof(currentRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)¤tRCVBUF, &optSize)) + { + currentRCVBUF = -1; + } + if (maxsize > currentRCVBUF) + { + ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&maxsize, sizeof maxsize); + } + } + if (0 != + ::setsockopt( + m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize)) + { + int currentSNDBUF = 0; + optSize = sizeof(currentSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)¤tSNDBUF, &optSize)) + { + currentSNDBUF = -1; + } + if (maxsize > currentSNDBUF) + { + ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&maxsize, sizeof maxsize); + } + } + + // Retrieve ending SND/RCV Buffer sizes. + int endRCVBUF = 0; + optSize = sizeof(endRCVBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (void*)&endRCVBUF, &optSize)) + { + endRCVBUF = -1; + } + int endSNDBUF = 0; + optSize = sizeof(endSNDBUF); + if (0 != ::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (void*)&endSNDBUF, &optSize)) + { + endSNDBUF = -1; + } + LOGC(kmlog.Debug, + log << "SO_RCVBUF:" + << " startRCVBUF=" << startRCVBUF << " m_mcfg.iUDPRcvBufSize=" << m_mcfg.iUDPRcvBufSize + << " endRCVBUF=" << endRCVBUF); + LOGC(kmlog.Debug, + log << "SO_SNDBUF:" + << " startSNDBUF=" << startSNDBUF << " m_mcfg.iUDPSndBufSize=" << m_mcfg.iUDPSndBufSize + << " endSNDBUF=" << endSNDBUF); + } +#elif defined(BSD) || TARGET_OS_MAC // BSD system will fail setsockopt if the requested buffer size exceeds system maximum value int maxsize = 64000; if (0 != ::setsockopt( From 8b68157d57a4df9f95ba02d37c3fce64c0d480c5 Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Wed, 1 Dec 2021 21:34:35 +0800 Subject: [PATCH 205/683] [core] fix CRcvLossList::m_iTail not reset to -1 --- srtcore/list.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index e295fb8c8..9459e99bd 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -583,6 +583,8 @@ bool CRcvLossList::remove(int32_t seqno) m_iHead = m_caSeq[m_iHead].inext; if (-1 != m_iHead) m_caSeq[m_iHead].iprior = -1; + else + m_iTail = -1; } else { From 244d2f41d1154b3790672a10ff1178edd68979ad Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Thu, 2 Dec 2021 15:48:21 +0800 Subject: [PATCH 206/683] [core] Fix deadlock introduced by CUDTGroup::setOpt() --- srtcore/group.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c27ae2cbe..a06be0fb8 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -444,10 +444,19 @@ void CUDTGroup::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) HLOGC(gmlog.Debug, log << "... SPREADING to existing sockets."); // This means that there are sockets already, so apply // this option on them. - ScopedLock gg(m_GroupLock); - for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + std::vector ps_vec; + { + // Do copy to avoid deadlock. CUDT::setOpt() cannot be called directly inside this loop, because + // CUDT::setOpt() will lock m_ConnectionLock, which should be locked before m_GroupLock. + ScopedLock gg(m_GroupLock); + for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + ps_vec.push_back(gi->ps); + } + } + for (std::vector::iterator it = ps_vec.begin(); it != ps_vec.end(); ++it) { - gi->ps->core().setOpt(optName, optval, optlen); + (*it)->core().setOpt(optName, optval, optlen); } } From c9a8db75e865925f156e0d1c3eeea02ec43e1208 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Fri, 3 Dec 2021 21:35:08 +0800 Subject: [PATCH 207/683] [core] Fix consistency of packet seqno in CRcvLossList (#2195) Make sure data to be inserted into CRcvLossList is monotonic, i.e. don't insert sequence numbers less (earlier) than the latest one. --- srtcore/list.cpp | 24 +++++++++++++++++++++++- srtcore/list.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 9459e99bd..ec63c8755 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -487,6 +487,7 @@ CRcvLossList::CRcvLossList(int size) , m_iTail(-1) , m_iLength(0) , m_iSize(size) + , m_iLargestSeq(SRT_SEQNO_NONE) { m_caSeq = new Seq[m_iSize]; @@ -506,7 +507,25 @@ CRcvLossList::~CRcvLossList() void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) { // Data to be inserted must be larger than all those in the list - // guaranteed by the UDT receiver + if (m_iLargestSeq != SRT_SEQNO_NONE && CSeqNo::seqcmp(seqno1, m_iLargestSeq) <= 0) + { + if (CSeqNo::seqcmp(seqno2, m_iLargestSeq) > 0) + { + LOGC(qrlog.Warn, + log << "RCV-LOSS/insert: seqno1=" << seqno1 << " too small, adjust to " + << CSeqNo::incseq(m_iLargestSeq)); + seqno1 = CSeqNo::incseq(m_iLargestSeq); + } + else + { + LOGC(qrlog.Warn, + log << "RCV-LOSS/insert: (" << seqno1 << "," << seqno2 + << ") to be inserted is too small: m_iLargestSeq=" << m_iLargestSeq << ", m_iLength=" << m_iLength + << ", m_iHead=" << m_iHead << ", m_iTail=" << m_iTail << " -- REJECTING"); + return; + } + } + m_iLargestSeq = seqno2; if (0 == m_iLength) { @@ -561,6 +580,9 @@ void CRcvLossList::insert(int32_t seqno1, int32_t seqno2) bool CRcvLossList::remove(int32_t seqno) { + if (m_iLargestSeq == SRT_SEQNO_NONE || CSeqNo::seqcmp(seqno, m_iLargestSeq) > 0) + m_iLargestSeq = seqno; + if (0 == m_iLength) return false; diff --git a/srtcore/list.h b/srtcore/list.h index 19b300f30..d79349aa7 100644 --- a/srtcore/list.h +++ b/srtcore/list.h @@ -185,6 +185,7 @@ class CRcvLossList int m_iTail; // last node in the list; int m_iLength; // loss length int m_iSize; // size of the static array + int m_iLargestSeq; // largest seq ever seen private: CRcvLossList(const CRcvLossList&); From 40b51294b94b0d2cc130bc5718d9ae5b8f2a6ab0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Dec 2021 10:51:47 +0100 Subject: [PATCH 208/683] [apps] Added support for adapter parameter in caller mode (#2202) --- apps/transmitmedia.cpp | 4 ++-- testing/testmedia.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index c61c703b8..da67bdaee 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -377,9 +377,9 @@ void SrtCommon::OpenClient(string host, int port) { PrepareClient(); - if ( m_outgoing_port ) + if (m_outgoing_port || m_adapter != "") { - SetupAdapter("", m_outgoing_port); + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(host, port); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 932406888..a5006c48d 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -881,9 +881,9 @@ void SrtCommon::OpenClient(string host, int port) { PrepareClient(); - if (m_outgoing_port) + if (m_outgoing_port || m_adapter != "") { - SetupAdapter("", m_outgoing_port); + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(host, port); @@ -2580,10 +2580,10 @@ void SrtModel::Establish(std::string& w_name) Verb() << "NO STREAM ID for SRT connection"; } - if (m_outgoing_port) + if (m_outgoing_port || m_adapter != "") { - Verb() << "Setting outgoing port: " << m_outgoing_port; - SetupAdapter("", m_outgoing_port); + Verb() << "Setting outgoing port: " << m_outgoing_port << " adapter:" << m_adapter; + SetupAdapter(m_adapter, m_outgoing_port); } ConnectClient(m_host, m_port); From 1d808c1501b426ac95cced21be8c12f78af31f4e Mon Sep 17 00:00:00 2001 From: "guangqing.chen" Date: Mon, 6 Dec 2021 20:20:57 +0800 Subject: [PATCH 209/683] [core] fix recv_WaitForReadReady() return empty --- srtcore/group.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index a06be0fb8..c9c01378a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2041,6 +2041,7 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& // This can only happen when 0 is passed as timeout and none is ready. // And 0 is passed only in non-blocking mode. So this is none ready in // non-blocking mode. + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); } @@ -2329,6 +2330,14 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) // m_GlobControlLock lifted, m_GroupLock still locked. // Now we can safely do this scoped way. + if (!m_bSynRecving && ready_sockets.empty()) + { + HLOGC(grlog.Debug, + log << "group/rcv $" << m_GroupID << ": Not available AT THIS TIME, NOT READ-READY now."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + // Ok, now we need to have some extra qualifications: // 1. If a socket has no registry yet, we read anyway, just // to notify the current position. We read ONLY ONE PACKET this time, From 545700fd81d3c121ec807ea4c6310dad3eb95eea Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Dec 2021 15:30:40 +0100 Subject: [PATCH 210/683] [docs] Added and updated description of useful test applications (#2191) --- docs/README.md | 14 ++-- docs/apps/srt-file-transmit.md | 55 +++++++++++++ ...srt-multiplex.md => srt-test-multiplex.md} | 81 ++++++++++--------- docs/apps/srt-test-relay.md | 34 ++++++++ docs/apps/srt-tunnel.md | 10 +-- 5 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 docs/apps/srt-file-transmit.md rename docs/apps/{srt-multiplex.md => srt-test-multiplex.md} (55%) create mode 100644 docs/apps/srt-test-relay.md diff --git a/docs/README.md b/docs/README.md index 55a632bcc..9f07f3e06 100644 --- a/docs/README.md +++ b/docs/README.md @@ -46,12 +46,14 @@ ## Sample Applications -| Document Title | Folder | File Name | Description | -| :----------------------------------------------------------- | :---------------------------- | :------------------------------------------------ | :----------------------------------------------------------- | -| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | -| [Using the
`srt-multiplex` App](apps/srt-multiplex.md) | [apps](apps/) | [srt-multiplex.md](apps/srt-multiplex.md) | Description of sample program for sending multiple streams. | -| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | -| | | | | +| Document Title | Folder | File Name | Description | +| :--------------------------------------------------------------------- | :-------------------- | :-------------------------------------------------- | :------------------------------------------------------------ | +| [Using the
`srt-live-transmit` App](apps/srt-live-transmit.md) | [apps](apps/) | [srt-live-transmit.md](apps/srt-live-transmit.md) | A sample application to transmit a live stream from
source medium (UDP/SRT/`stdin`) to the target medium
(UDP/SRT/`stdout`). | +| [Using the
`srt-file-transmit` App](apps/srt-file-transmit.md) | [apps](apps/) | [srt-file-transmit.md](apps/srt-file-transmit.md) | A sample application to transmit a file over SRT | +| [Using the
`srt-tunnel` App](apps/srt-tunnel.md) | [apps](apps/) | [srt-tunnel.md](apps/srt-tunnel.md) | A sample application to set up an SRT tunnel for TCP traffic. | +| [Using the
`srt-test-multiplex` App](apps/srt-test-multiplex.md) | [apps](apps/) | [srt-test-multiplex.md](apps/srt-test-multiplex.md) | Testing application that allows to send multiple streams over one UDP link. | +| [Using the
`srt-test-relay` App](apps/srt-test-relay.md) | [apps](apps/) | [srt-test-relay.md](apps/srt-test-relay.md) | Testing application for bidirectional stream sending over one connection. | +| | | | | ## Miscellaneous diff --git a/docs/apps/srt-file-transmit.md b/docs/apps/srt-file-transmit.md new file mode 100644 index 000000000..2bf3b1115 --- /dev/null +++ b/docs/apps/srt-file-transmit.md @@ -0,0 +1,55 @@ +# srt-file-transmit + +The `srt-file-transmit` tool is a tool for transmitting files over SRT. + +You need: + + - a file to transmit + - a destination to store it into + - this application run on both sides, one sending, one receiving + +## Introduction + +The `srt-file-transmit` application will transmit your file over the SRT connection, +the application on the other side will receive it and store to the desired location. +Both caller-listener and rendezvous arrangement of the connection are possible +and in whatever direction. + +The `streamid` socket option will be used to pass the filename to the other side +so that it is either written with this name or matched with the filename internally. + +The application the will be sending a file should use the file path as source and +SRT URI as a destination, and vice versa for receiving. + +## Caller mode + +If you use sender in caller mode, then the caller SRT URI destination should be +specified, and the "root name" from the file path will be set to `streamid`. +This will allow the listener to use it when writing. + +If a receiver is used as a caller, then the destination filepath's rootname +will be also passed as `streamid` so that the listener receiver can pick up the +file by name. + +In caller mode you must specify the full filename path of the received or sent +file. + +## Listener mode + +If you use sender in listener mode, then you start it with specifying either the +full filename path, or only the directory where the file is located; in the latter +case the filename will be tried from the `streamid` option extracted from the +connected socket (as set by the other side's caller). If the full filename was +specified, it must match the rootname extraced from this option, or otherwise +transmission will not be done. + +If you use receiver in listener mode, then you start it with specifying either +the full filename path, or just the directory. In the latter case the root name +will be extracted from `streamid` socket option, and this one will be transmitted. + +## Usage + +``` +srt-file-transmit [options] +``` + diff --git a/docs/apps/srt-multiplex.md b/docs/apps/srt-test-multiplex.md similarity index 55% rename from docs/apps/srt-multiplex.md rename to docs/apps/srt-test-multiplex.md index 80dbd0411..8e855a15b 100644 --- a/docs/apps/srt-multiplex.md +++ b/docs/apps/srt-test-multiplex.md @@ -1,26 +1,27 @@ -## srt-multiplex +# srt-test-multiplex -**srt-multiplex** (formerly called "SIPLEX") is a sample program that can send -multiple streams in one direction. This tool demonstrates two SRT features: +**srt-test-multiplex** (formerly called "SIPLEX") is a sample program that can +send multiple streams in one direction. This tool demonstrates two SRT features: - the ability to use a single UDP link (a source and destination pair specified by IP address and port) for multiple SRT connections - the use of the `streamid` socket option to identify multiplexed resources -NOTE: due to changes in the common code that can't be properly handled in the -current form of srt-multiplex, this application is temporarily blocked. Instead -the `srt-test-multiplex` application was added with the same functionality, -although it's recommended for testing purposes only. +NOTE: To make this application compiled, you need the `-DENABLE_TESTING=1` +cmake option. -#### Usage +Note also that this application is intended for demonstration only. It can +simply exit with error message in case of wrong usage or broken connection. -`srt-multiplex -i [id] [id]...` - - Multiplexes data from one or more input URIs to transmit as an SRT stream. - The application reads multiple streams (INPUT URIs), each using a separate SRT - connection. All of the traffic from these connections is sent through the - same UDP link. +## Usage -`srt-multiplex -o [id] [id]...` +`srt-test-multiplex -i [id1] [id2]...` + + - Reads all given input streams and sends them each one over a separate + SRT connection, all using the same remote address and source port (hence + using the same UDP link). + +`srt-test-multiplex -o [id] [id]...` - Transmits data from a multiplexed SRT stream to the specified output URI(s). @@ -29,41 +30,41 @@ options. When `-i` is specified, the URIs provided are used as input, and will be output over the `` socket. The reverse is true for any output URIs specified by `-o`. -Separate connections will be created for every specified URI, although all will -be using the same UDP link. When SRT is in caller mode, the SRT socket created -for transmitting data for a given URI will be set to the `streamid` socket option -from this URI's `id` parameter. When SRT is in listener mode, the `streamid` -option will already be set on the accepted socket, and will be matched with a -URI that has the same value in its `id` parameter. - -This `streamid` is the SRT socket option (`SRTO_STREAMID` in the API). The idea -is that it can be set on a socket used for connecting. When a listener is -getting an accepted socket for that connection, the `streamid` socket option -can be read from it, with the result that it will be the same as was set on -the caller side. - -So, in caller mode, for every stream media URI (input or output) there will be -a separate SRT socket created. This socket will have its `socketid` option -set to the value that is given by user as the `id` parameter attached to a -particular URI. In listener mode this happens in the opposite direction — the -value of the `streamid` option is extracted from the accepted socket, and then -matched against all ids specified with the stream media URIs: +If SRT-URI is caller mode, then for every declared input or output medium a +separate connection will be made, each one using the same source port (if +specified, the same will be used for all connections, otherwise the first one +will be automatically selected and then reused for all next ones) and with the +`streamid` socket option set to the value extracted from the medium's +`id` specified parameter: ``` URI1?id=a --> s1(streamid=a).connect(remhost:2000) URI2?id=b --> s2(streamid=b).connect(remhost:2000) URI3?id=c --> s3(streamid=c).connect(remhost:2000) ``` -And on the listener side: + +If SRT-URI is listener mode, then it will extract the value from `streamid` +socket option and every accepted connection will be matched against the `id` +parameter of the specified input or output medium. ``` (remhost:2000) -> accept --> s(SRT socket) --> in URI array find such that uri.id == s.streamid ``` -#### Examples +Note that the rendezvous mode is not supported because you cannot make +multiple connections over the same UDP link in rendezvous mode. + +This `streamid` is the SRT socket option (`SRTO_STREAMID` in the API). The idea +is that it can be set on a socket used for connecting. When a listener is +getting an accepted socket for that connection, the `streamid` socket option +can be read from it, with the result that it will be the same as was set on +the caller side. + + +## Examples - **Sender:** - - `srt-multiplex srt://remhost:2000 -i udp://:5000?id=low udp://:6000?id=high` + - `srt-test-multiplex srt://remhost:2000 -i udp://:5000?id=low udp://:6000?id=high` - **Receiver:** - - `srt-multiplex srt://:2000 -o output-high.ts?id=high output-low.ts?id=low` + - `srt-test-multiplex srt://:2000 -o output-high.ts?id=high output-low.ts?id=low` In this example a Sender is created which will connect to `remhost` port 2000 using multiple SRT sockets, all of which will be using the same outgoing port. @@ -72,7 +73,7 @@ sockets will reuse that port. Alternatively you can enforce the outgoing port using the `port` parameter with the ``. - **Sender:** - - `srt-multiplex srt://remhost:2000?port=5555 ...` + - `srt-test-multiplex srt://remhost:2000?port=5555 ...` A separate connection is made for every input resource. An appropriate resource ID will be set to each socket assigned to that resource according to the `id` @@ -90,9 +91,9 @@ port=5555 -| --------- ( multiplexed UDP stream ) ---------- |- port=2000 +-- --+ ``` When a socket is accepted on the listener side (the Receiver in this example), -srt-multiplex will search for the resource ID among the registered resources +srt-test-multiplex will search for the resource ID among the registered resources (input/output URIs) and set an ID that matches the one on the caller side. If the resource is not found, the connection is closed immediately. -The srt-multiplex program works the same way for connections initiated by a +The srt-test-multiplex program works the same way for connections initiated by a caller or a listener. diff --git a/docs/apps/srt-test-relay.md b/docs/apps/srt-test-relay.md new file mode 100644 index 000000000..85789b6db --- /dev/null +++ b/docs/apps/srt-test-relay.md @@ -0,0 +1,34 @@ +# srt-test-relay + +**srt-test-relay** is a sample program that can utilize SRT connection for +bidirectional traffic. Hence beside the SRT connection you can specify both +input and output media to and from which the traffic will be flipped. + +Effectively the specified input will be sent through the SRT connection as +output, and the input read from the SRT connection will be redirected to +the given output media. + +NOTE: To make this application compiled, you need the `-DENABLE_TESTING=1` +cmake option. + +Note also that this application is intended for demonstration only. It can +simply exit with error message in case of wrong usage or broken connection. + +## Usage + +`srt-test-relay -i -o ` + +Establish a connection, send to it the stream received from INPUT URI and +write the data read from the SRT connection to OUTPUT URI. + +`srt-test-relay -e -o ` + +Establish a connection, read the data from the SRT connection, and write +them back over the SRT connection, and additionally write them as well +to OUTPUT URI and OUTPUT URI2. + +Note that you can also control the single portion of data to be sent +at once by one sending call by `-c` option and you can use both live +and file mode for the connection (the latter should be simply enforced +by `transtype` socket option). + diff --git a/docs/apps/srt-tunnel.md b/docs/apps/srt-tunnel.md index b38c80038..c664173bf 100644 --- a/docs/apps/srt-tunnel.md +++ b/docs/apps/srt-tunnel.md @@ -1,8 +1,7 @@ -SRT Tunnel -========== +# srt-tunnel -Purpose -------- + +## Purpose SRT Tunnel is a typical tunnelling application, that is, it simply passes the transmission from a given endpoint to another endpoint in both directions. @@ -22,8 +21,7 @@ longer distance, leaving TCP close to the caller and listener locations: --> TCP> --> ``` -Usage ------ +## Usage The `srt-tunnel` command line accepts two argument, beside the options: * Listener: the URI at which this tunnel should await connections From c8cb38fe28e4202dc32e98b573acb56f49e54bf6 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 8 Dec 2021 19:08:17 +0800 Subject: [PATCH 211/683] [core] Fix m_GroupOf->updateReadState() in message mode (#2204) Fix ackDataUpTo(..) with the old RCV buffer. Co-authored-by: hondaxiao Co-authored-by: Maxim Sharabayko --- srtcore/buffer_rcv.h | 6 ++++-- srtcore/core.cpp | 16 +++++----------- srtcore/core.h | 6 ++++++ srtcore/group.h | 5 +++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 490d281d9..589cf2bc0 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -163,8 +163,10 @@ class CRcvBufferNew /// IF skipseqno == -1, no missing packet but 1st not ready to play. PacketInfo getFirstValidPacketInfo() const; - /// Get information on the packets available to be read - /// @returns a pair of sequence numbers + /// Get information on packets available to be read. + /// @returns a pair of sequence numbers (first available; first unavailable). + /// + /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; size_t countReadable() const; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b140c6422..01c234c32 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7734,7 +7734,7 @@ void srt::CUDT::releaseSynch() // [[using locked(m_RcvBufferLock)]]; int32_t srt::CUDT::ackDataUpTo(int32_t ack) { - const int acksize = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); + const int acksize SRT_ATR_UNUSED = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); HLOGC(xtlog.Debug, log << "ackDataUpTo: %" << m_iRcvLastSkipAck << " -> %" << ack << " (" << acksize << " packets)"); @@ -7750,21 +7750,15 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first << " - %" << range.second); } - return acksize; + return CSeqNo::decseq(range.second); #else // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. if (acksize > 0) { - const int distance = m_pRcvBuffer->ackData(acksize); - return CSeqNo::decseq(ack, distance); + m_pRcvBuffer->ackData(acksize); } - - // If nothing was confirmed, then use the current buffer span - const int distance = m_pRcvBuffer->getRcvDataSize(); - if (distance > 0) - return CSeqNo::decseq(ack, distance); return ack; #endif } @@ -8006,7 +8000,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t first_seq SRT_ATR_UNUSED = ackDataUpTo(ack); + const int32_t group_read_seq SRT_ATR_UNUSED = ackDataUpTo(ack); InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8101,7 +8095,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // The current "APP reader" needs to simply decide as to whether // the next CUDTGroup::recv() call should return with no blocking or not. // When the group is read-ready, it should update its pollers as it sees fit. - m_parent->m_GroupOf->updateReadState(m_SocketID, first_seq); + m_parent->m_GroupOf->updateReadState(m_SocketID, group_read_seq); } } #endif diff --git a/srtcore/core.h b/srtcore/core.h index 54c3927e2..71aef9f0a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1043,7 +1043,13 @@ class CUDT int processConnectRequest(const sockaddr_any& addr, CPacket& packet); static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0); + + /// @brief Acknowledge reading position up to the @p seq. + /// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq. + /// @param seq first unacknowledged packet sequence number. + /// @return int32_t ackDataUpTo(int32_t seq); + void handleKeepalive(const char* data, size_t lenghth); /// Locks m_RcvBufferLock and retrieves the available size of the receiver buffer. diff --git a/srtcore/group.h b/srtcore/group.h index 9927a2f6d..c61835e1b 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -341,7 +341,12 @@ class CUDTGroup void addEPoll(int eid); void removeEPollEvents(const int eid); void removeEPollID(const int eid); + + /// @brief Update read-ready state. + /// @param sock member socket ID (unused) + /// @param sequence the latest packet sequence number available for reading. void updateReadState(SRTSOCKET sock, int32_t sequence); + void updateWriteState(); void updateFailedLink(); void activateUpdateEvent(bool still_have_items); From e0aaa44331d7396369531bc65e1b12d70888a480 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 8 Dec 2021 11:04:54 +0100 Subject: [PATCH 212/683] [tests] Minor improvement of the TestSocketOptions StreamIDLenListener test case. --- test/test_socket_options.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index d010de510..654b45164 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -110,8 +110,6 @@ class TestSocketOptions } protected: - // put in any custom data members that you need - sockaddr_in m_sa; SRTSOCKET m_caller_sock = SRT_INVALID_SOCK; SRTSOCKET m_listen_sock = SRT_INVALID_SOCK; @@ -982,6 +980,8 @@ TEST_F(TestSocketOptions, StreamIDFull) ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); } +// Check that StreamID assigned to a listener socket is not inherited by accepted sockets, +// and is not derived by a caller socket. TEST_F(TestSocketOptions, StreamIDLenListener) { string stream_id_13 = "something1234"; @@ -991,18 +991,19 @@ TEST_F(TestSocketOptions, StreamIDLenListener) char buffer[648]; int buffer_len = sizeof buffer; EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); + EXPECT_EQ(string(buffer), stream_id_13); + EXPECT_EQ(size_t(buffer_len), stream_id_13.size()); StartListener(); const SRTSOCKET accepted_sock = EstablishConnection(); - // Check accepted socket inherits values + // Check accepted and caller sockets do not inherit StreamID. for (SRTSOCKET sock : { m_caller_sock, accepted_sock }) { - for (size_t i = 0; i < sizeof buffer; ++i) - buffer[i] = 'a'; buffer_len = (int)(sizeof buffer); + fill_n(buffer, buffer_len, 'a'); EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS); - EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "LISTENER"); + EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "CALLER"); } ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); From ae787bf23a79384638370c02cab60f7ae00c13f9 Mon Sep 17 00:00:00 2001 From: hondaxiao Date: Thu, 9 Dec 2021 21:28:36 +0800 Subject: [PATCH 213/683] [core] Fix rtt estimate in bidirectional mode --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 01c234c32..7e8839540 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8457,8 +8457,8 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - crttvar = avg_iir<4>(crttvar, abs(crtt - crtt)); - crtt = avg_iir<8>(crtt, crtt); + crttvar = avg_iir<4>(crttvar, abs(crtt - rtt)); + crtt = avg_iir<8>(crtt, rtt); } m_iSRTT = crtt; m_iRTTVar = crttvar; From 26678fe29b9204b1a353df986052c04b00967015 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Fri, 10 Dec 2021 17:19:59 +0100 Subject: [PATCH 214/683] [core] Fixed the issue with RTT in case of bidirectional transmission introduced when adding atomic types --- srtcore/core.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7e8839540..fc86d8a98 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8452,16 +8452,14 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // TODO: The case of bidirectional transmission requires further // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - - int crtt = m_iSRTT.load(), crttvar = m_iRTTVar.load(); - - if (crtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) { - crttvar = avg_iir<4>(crttvar, abs(crtt - rtt)); - crtt = avg_iir<8>(crtt, rtt); + int iSRTT = m_iSRTT.load(), iRTTVar = m_iRTTVar.load(); + iRTTVar = avg_iir<4>(iRTTVar, abs(rtt - iSRTT)); + iSRTT = avg_iir<8>(iSRTT, rtt); + m_iSRTT = iSRTT; + m_iRTTVar = iRTTVar; } - m_iSRTT = crtt; - m_iRTTVar = crttvar; } else // Transmission is unidirectional. { From cf34d69f0b595c76e8bc9ef9a12f1ba583f1a603 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 13 Dec 2021 12:04:54 +0100 Subject: [PATCH 215/683] [docs] Updated options description for srt-live-transmit (#2209) --- docs/apps/srt-live-transmit.md | 110 +++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 287a47649..258a5bb02 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -107,6 +107,16 @@ The specification and meaning of the fields in the URI depend on the mode. The **PORT** part is always mandatory and it designates either the port number for the target host or the port number to be bound to read from. +The following options are available through URI parameters: + +- **iptos**: sets the `IP_TOS` socket option +- **ttl**: sets the `IP_TTL` or `IP_MULTICAST_TTL` option, depending on mode +- **mcloop**: sets the `IP_MULTICAST_LOOP` option (multicast mode only) +- **rcvbuf**: sets the `SO_RCVBUF` socket option +- **sndbuf**: sets the `SO_SNDBUF` socket option +- **adapter**: sets the local binding address +- **source**: uses `IP_ADD_SOURCE_MEMBERSHIP`, see below for details + For sending to unicast: ```yaml @@ -173,7 +183,7 @@ options that can be set through the parameters. SRT can be connected using one of three connection modes: - **caller**: the "agent" (this application) sends the connection request to - the peer, which must be **listener**, and this way it initiates the +the peer, which must be **listener**, and this way it initiates the connection. - **listener**: the "agent" waits to be contacted by any peer **caller**. @@ -182,50 +192,73 @@ does not use this ability; after the first connection, it no longer accepts new connections. - **rendezvous**: A one-to-one only connection where both parties are - equivalent and both connect to one another simultaneously. Whoever happened -to start first (or succeeded to punch through the firewall) is meant to have +equivalent and both attempt to initiate a connection simultaneously. Whichever party happens +to start first (or succeeds in punching through the firewall first) is considered to have initiated the connection. This mode can be specified explicitly using the **mode** parameter. When it's -not specified, then it is "deduced" the following way: +not specified, then it is derived based on the *host* part in the URI and +the presence of the **adapter** parameter: -- `srt://:1234` - the *port* is specified (1234), but *host* is empty. This assumes **listener** mode. -- `srt://remote.host.com:1234` - both *host* and *port* are specified. This assumes **caller** mode. +* Listener mode: if you leave the *host* part empty (**adapter** may be specified): + - `srt://:1234` +* Caller mode: if you specify *host* part, but not **adapter** parameter: + - `srt://remote.host.com:1234` +* Rendezvous mode: if you specify *host* AND **adapter** parameter: + - `srt://remote.host.com:1234&adapter=my.remote.addr` -When the `mode` parameter is specified explicitly, then the interpretation of the `host` part is the following: +Sometimes the required parameter specification results in a different mode +than desired; in this case you should specify the mode explicitly. -- For caller, it's always the destination host address. If this is empty, it is -resolved to 0.0.0.0, which usually should mean connecting to the local host +The interpretation of the *host* and *port* parts is the following: -- For listener, it defines the IP address of the local device on which the -socket should listen, e.g.: +- In **LISTENER** mode: + - *host* part: the local IP address to bind (default: 0.0.0.0 - "all devices") + - *port* part: the local port to bind (mandatory) + - **adapter** parameter: alternative for *host* part, e.g.: ```yaml srt://10.10.10.100:5001?mode=listener ``` -An alternative method to specify this IP address is the `adapter` parameter: +or ```yaml srt://:5001?adapter=10.10.10.100 ``` -The **rendezvous** mode is not deduced and it has to be specified -explicitly. Note also special cases of the **host** and **port** parts -specified in the URI: - -- **CALLER**: the *host* and *port* parts are mandatory and specify the remote host and port to be contacted. - - The **port** parameter can be used to enforce the local outgoing port (**not to be confused** with remote port!). - - The **adapter** parameter is not used. -- **LISTENER**: the *port* part is mandatory and it specifies the local listening port. - - The **adapter** parameter can be used to specify the adapter. - - The *host* part, if specified, can be also used to set the adapter - although in this case **mode=listener** must be set explicitly. - - The **port** parameter is not used. -- **RENDEZVOUS**: the *host* and *port* parts are mandatory. - - The *host* part specifies the remote host to contact. - - The *port* part specifies **both local and remote port**. Note that the local port is this way both listening port and outgoing port. - - The **adapter** parameter can be used to specify the adapter. - - The **port** parameter can be used to specify the local port to bind to. +- In **CALLER** mode: + - *host* part: remote IP address to connect to (mandatory) + - *port* part: remote port to connect to (mandatory) + - **port** parameter: the local port to bind (default: 0 - "system autoselection") + - **adapter** parameter: the local IP address to bind (default: 0.0.0.0 - "system selected device") + +```yaml +srt://remote.host.com:5001 +``` + +```yaml +srt://remote.host.com:5001?adapter=local1&port=4001&mode=caller +``` + +- In **RENDEZVOUS** mode: same as **CALLER** except that the local +port, if not specified by the **port** parameter, defaults to the +value of the remote port (specified in the *port* part in the URI). + +```yaml +srt://remote.host.com:5001?mode=rendezvous +``` +(uses `remote.host.com` port 5001 for a remote host and the default +network device for routing to this host; the connection from the peer is +expected on that device and port 5001) + + +```yaml +srt://remote.host.com:5001?port=4001&adapter=local1 +``` +(uses `remote.host.com` port 5001 for a remote host and the peer +is expected to connect to `local1` address and port 4001) + **IMPORTANT** information about IPv6. @@ -238,14 +271,14 @@ the following restrictions: 2. In listener mode, if you leave the host empty, the socket is bound to `INADDR_ANY` for IPv4 only. If you want to make it listen on IPv6, you need to specify the host as `::`. -NOTE: Don't use square brackets syntax in the adapter specification, -as in this case only the host is expected. +NOTE: Don't use square brackets syntax in the **adapter** parameter +specification, as in this case only the host is expected. 3. If you want to listen for connections from both IPv4 and IPv6, mind the `ipv6only` option. The default value for this option is system default (see system manual for `IPV6_V6ONLY` socket option); if unsure, you might want to enforce `ipv6only=0` in order to be able to accept both IPv4 and IPv6 -connections in the same listener. +connections by the same listener, or set `ipv6only=1` to accept exclusively IPv6. 4. In rendezvous mode you may only interconnect both parties using IPv4, or both using IPv6. Unlike listener mode, if you want to leave the socket @@ -271,21 +304,22 @@ Examples: connection with local address `inaddr6_any` (IPv6) and port 4000 to a destination with port 5000. -* `srt://[::1]:5000?adapter=127.0.0.1&mode=rendezvous` - this URI is invalid - (different IP versions for binding and target address) +* `srt://[::1]:5000?adapter=127.0.0.1` - this URI is invalid + (different IP versions for binding and target address in rendezvous mode) -Some parameters handled for SRT medium are specific, all others are socket options. The following parameters are handled special way by `srt-live-transmit`: +Some parameters handled for SRT medium are specific, all others are socket options. The following parameters are handled in a special way by `srt-live-transmit`: - **mode**: enforce caller, listener or rendezvous mode -- **port**: enforce the **outgoing** port (the port number that will be set in the UDP packet as a source port when sent from this host). This can be used only in **caller mode**. +- **port**: enforce the **outgoing** port (the port number that will be set in the UDP packet as a source port when sent from this host). Not used in **listener** mode. - **blocking**: sets the `SRTO_RCVSYN` for input medium or `SRTO_SNDSYN` for output medium - **timeout**: sets `SRTO_RCVTIMEO` for input medium or `SRTO_SNDTIMEO` for output medium -- **adapter**: sets the adapter for listening in *listener* or *rendezvous* mode +- **adapter**: sets the local IP address to bind -All other parameters are SRT socket options. The following have the following value types: +All other parameters are SRT socket options. The Values column uses the +following type specification: - `bool`. Possible values: `yes`/`no`, `on`/`off`, `true`/`false`, `1`/`0`. -- `bytes` positive integer [1; INT32_MAX]. +- `bytes` positive integer `[1; INT32_MAX]`. - `ms` - positive integer value of milliseconds. | URI param | Values | SRT Option | Description | From 3558cd0dc4b3abf0de5c047ee233e0e91363dffa Mon Sep 17 00:00:00 2001 From: quink-black Date: Tue, 14 Dec 2021 21:41:53 +0800 Subject: [PATCH 216/683] [core] Fix GC stop handling (#1950) 1. Protect m_bClosing by m_GCStopLock 2. Fix the issue that m_GCStopLock can be set up multiple times and never be released. srt_startup()/srt_cleanup() meant to be called only once, but it isn't used this way in FFmpeg/VLC/GStreamer. --- srtcore/api.cpp | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 8e18bd23e..7e831391b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -196,6 +196,8 @@ srt::CUDTUnited::CUDTUnited(): // XXX An unlikely exception thrown from the below calls // might destroy the application before `main`. This shouldn't // be a problem in general. + setupMutex(m_GCStopLock, "GCStop"); + setupCond(m_GCStopCond, "GCStop"); setupMutex(m_GlobControlLock, "GlobControl"); setupMutex(m_IDLock, "ID"); setupMutex(m_InitLock, "Init"); @@ -216,6 +218,16 @@ srt::CUDTUnited::~CUDTUnited() releaseMutex(m_GlobControlLock); releaseMutex(m_IDLock); releaseMutex(m_InitLock); + // XXX There's some weird bug here causing this + // to hangup on Windows. This might be either something + // bigger, or some problem in pthread-win32. As this is + // the application cleanup section, this can be temporarily + // tolerated with simply exit the application without cleanup, + // counting on that the system will take care of it anyway. +#ifndef _WIN32 + releaseCond(m_GCStopCond); +#endif + releaseMutex(m_GCStopLock); delete m_pCache; } @@ -254,15 +266,6 @@ int srt::CUDTUnited::startup() m_bClosing = false; - try - { - setupMutex(m_GCStopLock, "GCStop"); - setupCond(m_GCStopCond, "GCStop"); - } - catch (...) - { - return -1; - } if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) return -1; @@ -291,7 +294,10 @@ int srt::CUDTUnited::cleanup() if (!m_bGCStatus) return 0; - m_bClosing = true; + { + UniqueLock gclock(m_GCStopLock); + m_bClosing = true; + } // NOTE: we can do relaxed signaling here because // waiting on m_GCStopCond has a 1-second timeout, // after which the m_bClosing flag is cheched, which @@ -300,16 +306,6 @@ int srt::CUDTUnited::cleanup() CSync::signal_relaxed(m_GCStopCond); m_GCThread.join(); - // XXX There's some weird bug here causing this - // to hangup on Windows. This might be either something - // bigger, or some problem in pthread-win32. As this is - // the application cleanup section, this can be temporarily - // tolerated with simply exit the application without cleanup, - // counting on that the system will take care of it anyway. -#ifndef _WIN32 - releaseCond(m_GCStopCond); -#endif - m_bGCStatus = false; // Global destruction code From 5f7bc23f728b1fd5e5ca8e9c4b4b3ba6f3df7b59 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 20 Dec 2021 10:15:10 +0100 Subject: [PATCH 217/683] [core] Refactored the core stats structure (#2212). Group stats are moved to the new structure. --- srtcore/common.cpp | 7 - srtcore/common.h | 86 ------------ srtcore/core.cpp | 292 +++++++++++++-------------------------- srtcore/core.h | 66 +-------- srtcore/filelist.maf | 1 + srtcore/group.cpp | 40 +++--- srtcore/group.h | 30 ++-- srtcore/packetfilter.cpp | 9 +- srtcore/stats.h | 222 +++++++++++++++++++++++++++++ 9 files changed, 361 insertions(+), 392 deletions(-) create mode 100644 srtcore/stats.h diff --git a/srtcore/common.cpp b/srtcore/common.cpp index cd151116a..27dbe89d1 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -503,13 +503,6 @@ bool SrtParseConfig(string s, SrtConfig& w_config) return true; } -uint64_t PacketMetric::fullBytes() -{ - static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - return bytes + pkts * PKT_HDR_SIZE; -} - - namespace srt_logging { diff --git a/srtcore/common.h b/srtcore/common.h index 3e2ce9363..60a884f5c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1414,90 +1414,4 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(std::string s, srt::SrtConfig& w_config); -struct PacketMetric -{ - uint32_t pkts; - uint64_t bytes; - - void update(uint64_t size) - { - ++pkts; - bytes += size; - } - - void update(size_t mult, uint64_t value) - { - pkts += (uint32_t) mult; - bytes += mult * value; - } - - uint64_t fullBytes(); -}; - -template -struct MetricOp; - -template -struct MetricUsage -{ - METRIC_TYPE local; - METRIC_TYPE total; - - void Clear() - { - MetricOp::Clear(local); - } - - void Init() - { - MetricOp::Clear(total); - Clear(); - } - - void Update(uint64_t value) - { - local += value; - total += value; - } - - void UpdateTimes(size_t mult, uint64_t value) - { - local += mult * value; - total += mult * value; - } -}; - -template <> -inline void MetricUsage::Update(uint64_t value) -{ - local.update(value); - total.update(value); -} - -template <> -inline void MetricUsage::UpdateTimes(size_t mult, uint64_t value) -{ - local.update(mult, value); - total.update(mult, value); -} - -template -struct MetricOp -{ - static void Clear(METRIC_TYPE& m) - { - m = 0; - } -}; - -template <> -struct MetricOp -{ - static void Clear(PacketMetric& p) - { - p.pkts = 0; - p.bytes = 0; - } -}; - #endif diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fc86d8a98..98b412f4e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -875,49 +875,11 @@ void srt::CUDT::clearData() ScopedLock stat_lock(m_StatsLock); m_stats.tsStartTime = steady_clock::now(); - m_stats.sentTotal = m_stats.sentUniqTotal = m_stats.recvTotal = m_stats.recvUniqTotal - = m_stats.sndLossTotal = m_stats.rcvLossTotal = m_stats.retransTotal - = m_stats.sentACKTotal = m_stats.recvACKTotal = m_stats.sentNAKTotal = m_stats.recvNAKTotal = 0; - m_stats.tsLastSampleTime = steady_clock::now(); - m_stats.traceSent = m_stats.traceSentUniq = m_stats.traceRecv = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceReorderDistance = 0; - m_stats.traceBelatedTime = 0.0; - m_stats.traceRcvBelated = 0; - - m_stats.sndDropTotal = 0; - m_stats.traceSndDrop = 0; - m_stats.rcvDropTotal = 0; - m_stats.traceRcvDrop = 0; - - m_stats.m_rcvUndecryptTotal = 0; - m_stats.traceRcvUndecrypt = 0; - - m_stats.bytesSentTotal = 0; - m_stats.bytesSentUniqTotal = 0; - m_stats.bytesRecvTotal = 0; - m_stats.bytesRecvUniqTotal = 0; - m_stats.bytesRetransTotal = 0; - m_stats.traceBytesSent = 0; - m_stats.traceBytesSentUniq = 0; - m_stats.traceBytesRecv = 0; - m_stats.traceBytesRecvUniq = 0; - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; - - m_stats.traceBytesRetrans = 0; - m_stats.traceRcvBytesLoss = 0; - m_stats.sndBytesDropTotal = 0; - m_stats.rcvBytesDropTotal = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.m_rcvBytesUndecryptTotal = 0; - m_stats.traceRcvBytesUndecrypt = 0; + m_stats.sndr.reset(); + m_stats.rcvr.reset(); + m_stats.tsLastSampleTime = steady_clock::now(); + m_stats.traceReorderDistance = 0; m_stats.sndDuration = m_stats.m_sndDurationTotal = 0; } @@ -5579,14 +5541,10 @@ void * srt::CUDT::tsbpd(void *param) void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { - /* Update drop/skip stats */ enterCS(m_StatsLock); - m_stats.rcvDropTotal += seqlen; - m_stats.traceRcvDrop += seqlen; - /* Estimate dropped/skipped bytes from average payload */ + // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvBytesDropTotal += seqlen * avgpayloadsz; - m_stats.traceRcvBytesDrop += seqlen * avgpayloadsz; + m_stats.rcvr.dropped.count(stats::BytesPackets(seqlen * avgpayloadsz, (size_t) seqlen)); leaveCS(m_StatsLock); dropFromLossLists(lastack, CSeqNo::decseq(skiptoseqno)); //remove(from,to-inclusive) @@ -6401,10 +6359,7 @@ bool srt::CUDT::checkNeedDrop() if (dpkts > 0) { enterCS(m_StatsLock); - m_stats.traceSndDrop += dpkts; - m_stats.sndDropTotal += dpkts; - m_stats.traceSndBytesDrop += dbytes; - m_stats.sndBytesDropTotal += dbytes; + m_stats.sndr.dropped.count(stats::BytesPackets(dbytes, dpkts)); leaveCS(m_StatsLock); IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); @@ -7364,76 +7319,76 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) const steady_clock::time_point currtime = steady_clock::now(); perf->msTimeStamp = count_milliseconds(currtime - m_stats.tsStartTime); - perf->pktSent = m_stats.traceSent; - perf->pktSentUnique = m_stats.traceSentUniq; - perf->pktRecv = m_stats.traceRecv; - perf->pktRecvUnique = m_stats.traceRecvUniq; - perf->pktSndLoss = m_stats.traceSndLoss; - perf->pktRcvLoss = m_stats.traceRcvLoss; - perf->pktRetrans = m_stats.traceRetrans; - perf->pktRcvRetrans = m_stats.traceRcvRetrans; - perf->pktSentACK = m_stats.sentACK; - perf->pktRecvACK = m_stats.recvACK; - perf->pktSentNAK = m_stats.sentNAK; - perf->pktRecvNAK = m_stats.recvNAK; + perf->pktSent = m_stats.sndr.sent.trace.count(); + perf->pktSentUnique = m_stats.sndr.sentUnique.trace.count(); + perf->pktRecv = m_stats.rcvr.recvd.trace.count(); + perf->pktRecvUnique = m_stats.rcvr.recvdUnique.trace.count(); + + perf->pktSndLoss = m_stats.sndr.lost.trace.count(); + perf->pktRcvLoss = m_stats.rcvr.lost.trace.count(); + perf->pktRetrans = m_stats.sndr.sentRetrans.trace.count(); + perf->pktRcvRetrans = m_stats.rcvr.recvdRetrans.trace.count(); + perf->pktSentACK = m_stats.rcvr.sentAck.trace.count(); + perf->pktRecvACK = m_stats.sndr.recvdAck.trace.count(); + perf->pktSentNAK = m_stats.rcvr.sentNak.trace.count(); + perf->pktRecvNAK = m_stats.sndr.recvdNak.trace.count(); perf->usSndDuration = m_stats.sndDuration; perf->pktReorderDistance = m_stats.traceReorderDistance; perf->pktReorderTolerance = m_iReorderTolerance; perf->pktRcvAvgBelatedTime = m_stats.traceBelatedTime; - perf->pktRcvBelated = m_stats.traceRcvBelated; + perf->pktRcvBelated = m_stats.rcvr.recvdBelated.trace.count(); - perf->pktSndFilterExtra = m_stats.sndFilterExtra; - perf->pktRcvFilterExtra = m_stats.rcvFilterExtra; - perf->pktRcvFilterSupply = m_stats.rcvFilterSupply; - perf->pktRcvFilterLoss = m_stats.rcvFilterLoss; + perf->pktSndFilterExtra = m_stats.sndr.sentFilterExtra.trace.count(); + perf->pktRcvFilterExtra = m_stats.rcvr.recvdFilterExtra.trace.count(); + perf->pktRcvFilterSupply = m_stats.rcvr.suppliedByFilter.trace.count(); + perf->pktRcvFilterLoss = m_stats.rcvr.lossFilter.trace.count(); /* perf byte counters include all headers (SRT+UDP+IP) */ - perf->byteSent = m_stats.traceBytesSent + (m_stats.traceSent * pktHdrSize); - perf->byteSentUnique = m_stats.traceBytesSentUniq + (m_stats.traceSentUniq * pktHdrSize); - perf->byteRecv = m_stats.traceBytesRecv + (m_stats.traceRecv * pktHdrSize); - perf->byteRecvUnique = m_stats.traceBytesRecvUniq + (m_stats.traceRecvUniq * pktHdrSize); - perf->byteRetrans = m_stats.traceBytesRetrans + (m_stats.traceRetrans * pktHdrSize); - perf->byteRcvLoss = m_stats.traceRcvBytesLoss + (m_stats.traceRcvLoss * pktHdrSize); - - perf->pktSndDrop = m_stats.traceSndDrop; - perf->pktRcvDrop = m_stats.traceRcvDrop + m_stats.traceRcvUndecrypt; - perf->byteSndDrop = m_stats.traceSndBytesDrop + (m_stats.traceSndDrop * pktHdrSize); - perf->byteRcvDrop = - m_stats.traceRcvBytesDrop + (m_stats.traceRcvDrop * pktHdrSize) + m_stats.traceRcvBytesUndecrypt; - perf->pktRcvUndecrypt = m_stats.traceRcvUndecrypt; - perf->byteRcvUndecrypt = m_stats.traceRcvBytesUndecrypt; - - perf->pktSentTotal = m_stats.sentTotal; - perf->pktSentUniqueTotal = m_stats.sentUniqTotal; - perf->pktRecvTotal = m_stats.recvTotal; - perf->pktRecvUniqueTotal = m_stats.recvUniqTotal; - perf->pktSndLossTotal = m_stats.sndLossTotal; - perf->pktRcvLossTotal = m_stats.rcvLossTotal; - perf->pktRetransTotal = m_stats.retransTotal; - perf->pktSentACKTotal = m_stats.sentACKTotal; - perf->pktRecvACKTotal = m_stats.recvACKTotal; - perf->pktSentNAKTotal = m_stats.sentNAKTotal; - perf->pktRecvNAKTotal = m_stats.recvNAKTotal; + perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr(); + perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr(); + perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr(); + perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr(); + perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr(); + perf->byteRcvLoss = m_stats.rcvr.recvd.trace.bytesWithHdr(); + + perf->pktSndDrop = m_stats.sndr.dropped.trace.count(); + perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count() + m_stats.rcvr.undecrypted.trace.count(); + perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(); + perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(); + perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count(); + perf->byteRcvUndecrypt = m_stats.rcvr.undecrypted.trace.bytes(); + + perf->pktSentTotal = m_stats.sndr.sent.total.count(); + perf->pktSentUniqueTotal = m_stats.sndr.sentUnique.total.count(); + perf->pktRecvTotal = m_stats.rcvr.recvd.total.count(); + perf->pktRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.count(); + perf->pktSndLossTotal = m_stats.sndr.lost.total.count(); + perf->pktRcvLossTotal = m_stats.rcvr.lost.total.count(); + perf->pktRetransTotal = m_stats.sndr.sentRetrans.total.count(); + perf->pktSentACKTotal = m_stats.rcvr.sentAck.total.count(); + perf->pktRecvACKTotal = m_stats.sndr.recvdAck.total.count(); + perf->pktSentNAKTotal = m_stats.rcvr.sentNak.total.count(); + perf->pktRecvNAKTotal = m_stats.sndr.recvdNak.total.count(); perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - perf->byteSentTotal = m_stats.bytesSentTotal + (m_stats.sentTotal * pktHdrSize); - perf->byteSentUniqueTotal = m_stats.bytesSentUniqTotal + (m_stats.sentUniqTotal * pktHdrSize); - perf->byteRecvTotal = m_stats.bytesRecvTotal + (m_stats.recvTotal * pktHdrSize); - perf->byteRecvUniqueTotal = m_stats.bytesRecvUniqTotal + (m_stats.recvUniqTotal * pktHdrSize); - perf->byteRetransTotal = m_stats.bytesRetransTotal + (m_stats.retransTotal * pktHdrSize); - perf->pktSndFilterExtraTotal = m_stats.sndFilterExtraTotal; - perf->pktRcvFilterExtraTotal = m_stats.rcvFilterExtraTotal; - perf->pktRcvFilterSupplyTotal = m_stats.rcvFilterSupplyTotal; - perf->pktRcvFilterLossTotal = m_stats.rcvFilterLossTotal; - - perf->byteRcvLossTotal = m_stats.rcvBytesLossTotal + (m_stats.rcvLossTotal * pktHdrSize); - perf->pktSndDropTotal = m_stats.sndDropTotal; - perf->pktRcvDropTotal = m_stats.rcvDropTotal + m_stats.m_rcvUndecryptTotal; - perf->byteSndDropTotal = m_stats.sndBytesDropTotal + (m_stats.sndDropTotal * pktHdrSize); - perf->byteRcvDropTotal = - m_stats.rcvBytesDropTotal + (m_stats.rcvDropTotal * pktHdrSize) + m_stats.m_rcvBytesUndecryptTotal; - perf->pktRcvUndecryptTotal = m_stats.m_rcvUndecryptTotal; - perf->byteRcvUndecryptTotal = m_stats.m_rcvBytesUndecryptTotal; + perf->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr(); + perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr(); + perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr(); + perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr(); + perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr(); + perf->pktSndFilterExtraTotal = m_stats.sndr.sentFilterExtra.total.count(); + perf->pktRcvFilterExtraTotal = m_stats.rcvr.recvdFilterExtra.total.count(); + perf->pktRcvFilterSupplyTotal = m_stats.rcvr.suppliedByFilter.total.count(); + perf->pktRcvFilterLossTotal = m_stats.rcvr.lossFilter.total.count(); + + perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(); + perf->pktSndDropTotal = m_stats.sndr.dropped.total.count(); + perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count() + m_stats.rcvr.undecrypted.total.count(); + // TODO: The payload is dropped. Probably header sizes should not be counted? + perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(); + perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr() + m_stats.rcvr.undecrypted.total.bytesWithHdr(); + perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); + perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); // TODO: The following class members must be protected with a different mutex, not the m_StatsLock. const double interval = (double) count_microseconds(currtime - m_stats.tsLastSampleTime); @@ -7454,29 +7409,10 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (clear) { - m_stats.traceSndDrop = 0; - m_stats.traceRcvDrop = 0; - m_stats.traceSndBytesDrop = 0; - m_stats.traceRcvBytesDrop = 0; - m_stats.traceRcvUndecrypt = 0; - m_stats.traceRcvBytesUndecrypt = 0; - m_stats.traceBytesSent = m_stats.traceBytesRecv = m_stats.traceBytesRetrans = 0; - m_stats.traceBytesSentUniq = m_stats.traceBytesRecvUniq = 0; - m_stats.traceSent = m_stats.traceRecv - = m_stats.traceSentUniq = m_stats.traceRecvUniq - = m_stats.traceSndLoss = m_stats.traceRcvLoss = m_stats.traceRetrans - = m_stats.sentACK = m_stats.recvACK = m_stats.sentNAK = m_stats.recvNAK = 0; - m_stats.sndDuration = 0; - m_stats.traceRcvRetrans = 0; - m_stats.traceRcvBelated = 0; - m_stats.traceRcvBytesLoss = 0; - - m_stats.sndFilterExtra = 0; - m_stats.rcvFilterExtra = 0; - - m_stats.rcvFilterSupply = 0; - m_stats.rcvFilterLoss = 0; + m_stats.sndr.resetTrace(); + m_stats.rcvr.resetTrace(); + m_stats.sndDuration = 0; m_stats.tsLastSampleTime = currtime; } } @@ -7833,8 +7769,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); - ++m_stats.sentNAK; - ++m_stats.sentNAKTotal; + m_stats.rcvr.sentNak.count(1); leaveCS(m_StatsLock); } // Call with no arguments - get loss list from internal data. @@ -7855,8 +7790,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); - ++m_stats.sentNAK; - ++m_stats.sentNAKTotal; + m_stats.rcvr.sentNak.count(1); leaveCS(m_StatsLock); } @@ -8183,8 +8117,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) m_ACKWindow.store(m_iAckSeqNo, m_iRcvLastAck); enterCS(m_StatsLock); - ++m_stats.sentACK; - ++m_stats.sentACKTotal; + m_stats.rcvr.sentAck.count(1); leaveCS(m_StatsLock); } else @@ -8439,7 +8372,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // Suppose transmission is bidirectional if sender is also receiving // data packets. enterCS(m_StatsLock); - const bool bPktsReceived = m_stats.recvTotal != 0; + const bool bPktsReceived = m_stats.rcvr.recvd.total.count() != 0; leaveCS(m_StatsLock); if (bPktsReceived) // Transmission is bidirectional. @@ -8532,8 +8465,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ updateCC(TEV_ACK, EventVariant(ackdata_seqno)); enterCS(m_StatsLock); - ++m_stats.recvACK; - ++m_stats.recvACKTotal; + m_stats.sndr.recvdAck.count(1); leaveCS(m_StatsLock); } @@ -8719,8 +8651,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) } enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); } else if (CSeqNo::seqcmp(losslist[i], m_iSndLastAck) >= 0) @@ -8740,8 +8671,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) const int num = m_pSndLossList->insert(losslist[i], losslist[i]); enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); } } @@ -8764,8 +8694,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); enterCS(m_StatsLock); - ++m_stats.recvNAK; - ++m_stats.recvNAKTotal; + m_stats.sndr.recvdNak.count(1); leaveCS(m_StatsLock); } @@ -9231,10 +9160,7 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi ackguard.unlock(); enterCS(m_StatsLock); - ++m_stats.traceRetrans; - ++m_stats.retransTotal; - m_stats.traceBytesRetrans += payload; - m_stats.bytesRetransTotal += payload; + m_stats.sndr.sentRetrans.count(payload); leaveCS(m_StatsLock); // Despite the contextual interpretation of packet.m_iMsgNo around @@ -9298,8 +9224,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Stats { ScopedLock lg(m_StatsLock); - ++m_stats.sndFilterExtra; - ++m_stats.sndFilterExtraTotal; + m_stats.sndr.sentFilterExtra.count(1); } } else @@ -9500,17 +9425,9 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); enterCS(m_StatsLock); - m_stats.traceBytesSent += payload; - m_stats.bytesSentTotal += payload; - ++m_stats.traceSent; - ++m_stats.sentTotal; + m_stats.sndr.sent.count(payload); if (new_packet_packed) - { - ++m_stats.traceSentUniq; - ++m_stats.sentUniqTotal; - m_stats.traceBytesSentUniq += payload; - m_stats.bytesSentUniqTotal += payload; - } + m_stats.sndr.sentUnique.count(payload); leaveCS(m_StatsLock); if (probe) @@ -9707,7 +9624,7 @@ int srt::CUDT::processData(CUnit* in_unit) { // This packet was retransmitted enterCS(m_StatsLock); - m_stats.traceRcvRetrans++; + m_stats.rcvr.recvdRetrans.count(packet.getLength()); leaveCS(m_StatsLock); #if ENABLE_HEAVY_LOGGING @@ -9767,10 +9684,7 @@ int srt::CUDT::processData(CUnit* in_unit) m_RcvTimeWindow.probeArrival(packet, unordered || retransmitted); enterCS(m_StatsLock); - m_stats.traceBytesRecv += pktsz; - m_stats.bytesRecvTotal += pktsz; - ++m_stats.traceRecv; - ++m_stats.recvTotal; + m_stats.rcvr.recvd.count(pktsz); leaveCS(m_StatsLock); loss_seqs_t filter_loss_seqs; @@ -9809,14 +9723,11 @@ int srt::CUDT::processData(CUnit* in_unit) // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { + const int loss = diff - 1; // loss is all that is above diff == 1 ScopedLock lg(m_StatsLock); - int loss = diff - 1; // loss is all that is above diff == 1 - m_stats.traceRcvLoss += loss; - m_stats.rcvLossTotal += loss; const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - const uint64_t lossbytes = loss * avgpayloadsz; - m_stats.traceRcvBytesLoss += lossbytes; - m_stats.rcvBytesLossTotal += lossbytes; + m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, loss)); + HLOGC(qrlog.Debug, log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); @@ -9927,7 +9838,7 @@ int srt::CUDT::processData(CUnit* in_unit) enterCS(m_StatsLock); m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.traceRcvBelated++; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" @@ -10006,22 +9917,13 @@ int srt::CUDT::processData(CUnit* in_unit) EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; if (rc != ENCS_CLEAR) { - // Could not decrypt - // Keep packet in received buffer - // Crypto flags are still set - // It will be acknowledged - { - ScopedLock lg(m_StatsLock); - m_stats.traceRcvUndecrypt += 1; - m_stats.traceRcvBytesUndecrypt += pktsz; - m_stats.m_rcvUndecryptTotal += 1; - m_stats.m_rcvBytesUndecryptTotal += pktsz; - } - - // Log message degraded to debug because it may happen very often + // Heavy log message because if seen once the message may happen very often. HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); adding_successful = false; IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + ScopedLock lg(m_StatsLock); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); } } } @@ -10029,10 +9931,7 @@ int srt::CUDT::processData(CUnit* in_unit) if (adding_successful) { ScopedLock statslock(m_StatsLock); - ++m_stats.traceRecvUniq; - ++m_stats.recvUniqTotal; - m_stats.traceBytesRecvUniq += u->m_Packet.getLength(); - m_stats.bytesRecvUniqTotal += u->m_Packet.getLength(); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); } #if ENABLE_HEAVY_LOGGING @@ -11247,8 +11146,7 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) if (num > 0) { enterCS(m_StatsLock); - m_stats.traceSndLoss += num; - m_stats.sndLossTotal += num; + m_stats.sndr.lost.count(num); leaveCS(m_StatsLock); HLOGC(xtlog.Debug, diff --git a/srtcore/core.h b/srtcore/core.h index 71aef9f0a..08056fec7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -73,6 +73,8 @@ modified by #include "utilities.h" #include "logger_defs.h" +#include "stats.h" + #include @@ -1065,73 +1067,17 @@ class CUDT struct CoreStats { time_point tsStartTime; // timestamp when the UDT entity is started - int64_t sentTotal; // total number of sent data packets, including retransmissions - int64_t sentUniqTotal; // total number of sent data packets, excluding rexmit and filter control - int64_t recvTotal; // total number of received packets - int64_t recvUniqTotal; // total number of received and delivered packets - int sndLossTotal; // total number of lost packets (sender side) - int rcvLossTotal; // total number of lost packets (receiver side) - int retransTotal; // total number of retransmitted packets - int sentACKTotal; // total number of sent ACK packets - int recvACKTotal; // total number of received ACK packets - int sentNAKTotal; // total number of sent NAK packets - int recvNAKTotal; // total number of received NAK packets - int sndDropTotal; - int rcvDropTotal; - uint64_t bytesSentTotal; // total number of bytes sent, including retransmissions - uint64_t bytesSentUniqTotal; // total number of bytes sent, including retransmissions - uint64_t bytesRecvTotal; // total number of received bytes - uint64_t bytesRecvUniqTotal; // total number of received bytes - uint64_t rcvBytesLossTotal; // total number of loss bytes (estimate) - uint64_t bytesRetransTotal; // total number of retransmitted bytes - uint64_t sndBytesDropTotal; - uint64_t rcvBytesDropTotal; - int m_rcvUndecryptTotal; - uint64_t m_rcvBytesUndecryptTotal; - - int sndFilterExtraTotal; - int rcvFilterExtraTotal; - int rcvFilterSupplyTotal; - int rcvFilterLossTotal; + stats::Sender sndr; // sender statistics + stats::Receiver rcvr; // receiver statistics int64_t m_sndDurationTotal; // total real time for sending time_point tsLastSampleTime; // last performance sample time - int64_t traceSent; // number of packets sent in the last trace interval - int64_t traceSentUniq; // number of original packets sent in the last trace interval - int64_t traceRecv; // number of packets received in the last trace interval - int64_t traceRecvUniq; // number of packets received AND DELIVERED in the last trace interval - int traceSndLoss; // number of lost packets in the last trace interval (sender side) - int traceRcvLoss; // number of lost packets in the last trace interval (receiver side) - int traceRetrans; // number of retransmitted packets in the last trace interval - int sentACK; // number of ACKs sent in the last trace interval - int recvACK; // number of ACKs received in the last trace interval - int sentNAK; // number of NAKs sent in the last trace interval - int recvNAK; // number of NAKs received in the last trace interval - int traceSndDrop; - int traceRcvDrop; - int traceRcvRetrans; int traceReorderDistance; double traceBelatedTime; - int64_t traceRcvBelated; - uint64_t traceBytesSent; // number of bytes sent in the last trace interval - uint64_t traceBytesSentUniq; // number of bytes sent in the last trace interval - uint64_t traceBytesRecv; // number of bytes sent in the last trace interval - uint64_t traceBytesRecvUniq; // number of bytes sent in the last trace interval - uint64_t traceRcvBytesLoss; // number of bytes bytes lost in the last trace interval (estimate) - uint64_t traceBytesRetrans; // number of bytes retransmitted in the last trace interval - uint64_t traceSndBytesDrop; - uint64_t traceRcvBytesDrop; - int traceRcvUndecrypt; - uint64_t traceRcvBytesUndecrypt; - - int sndFilterExtra; - int rcvFilterExtra; - int rcvFilterSupply; - int rcvFilterLoss; - + int64_t sndDuration; // real time for sending - time_point sndDurationCounter; // timers to record the sending Duration + time_point sndDurationCounter; // timers to record the sending Duration } m_stats; public: diff --git a/srtcore/filelist.maf b/srtcore/filelist.maf index 16fb48d45..b800fdc44 100644 --- a/srtcore/filelist.maf +++ b/srtcore/filelist.maf @@ -72,6 +72,7 @@ queue.h congctl.h socketconfig.h srt_compat.h +stats.h threadname.h tsbpd_time.h utilities.h diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c9c01378a..b9c18090a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1692,7 +1692,7 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Now that at least one link has succeeded, update sending stats. - m_stats.sent.Update(len); + m_stats.sent.count(len); // Pity that the blocking mode only determines as to whether this function should // block or not, but the epoll flags must be updated regardless of the mode. @@ -2259,7 +2259,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) pos->packet.clear(); // Update stats as per delivery - m_stats.recv.Update(len); + m_stats.recv.count(len); updateAvgPayloadSize(len); // We predict to have only one packet ahead, others are pending to be reported by tsbpd. @@ -2491,7 +2491,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) log << "group/recv: @" << id << " %" << mctrl.pktseq << " #" << mctrl.msgno << " BEHIND base=%" << m_RcvBaseSeqNo << " - discarding"); // The sequence is recorded, the packet has to be discarded. - m_stats.recvDiscard.Update(stat); + m_stats.recvDiscard.count(stat); continue; } @@ -2527,7 +2527,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) fillGroupData((w_mc), mctrl); // Update stats as per delivery - m_stats.recv.Update(output_size); + m_stats.recv.count(output_size); updateAvgPayloadSize(output_size); // Record, but do not update yet, until all sockets are handled. @@ -2681,7 +2681,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) const int32_t jump = (CSeqNo(slowest_kangaroo->second.mctrl.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; if (jump > 0) { - m_stats.recvDrop.UpdateTimes(jump, avgRcvPacketSize()); + m_stats.recvDrop.count(stats::BytesPackets(jump * static_cast(avgRcvPacketSize()), jump)); LOGC(grlog.Warn, log << "@" << m_GroupID << " GROUP RCV-DROPPED " << jump << " packet(s): seqno %" << m_RcvBaseSeqNo << " to %" << slowest_kangaroo->second.mctrl.pktseq); @@ -2703,7 +2703,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) pkt.clear(); // Update stats as per delivery - m_stats.recv.Update(len); + m_stats.recv.count(len); updateAvgPayloadSize(len); // It is unlikely to have a packet ahead because usually having one packet jumped-ahead @@ -2830,21 +2830,21 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) perf->msTimeStamp = count_milliseconds(currtime - m_tsStartTime); - perf->pktSentUnique = m_stats.sent.local.pkts; - perf->pktRecvUnique = m_stats.recv.local.pkts; - perf->pktRcvDrop = m_stats.recvDrop.local.pkts; + perf->pktSentUnique = m_stats.sent.trace.count(); + perf->pktRecvUnique = m_stats.recv.trace.count(); + perf->pktRcvDrop = m_stats.recvDrop.trace.count(); - perf->byteSentUnique = m_stats.sent.local.fullBytes(); - perf->byteRecvUnique = m_stats.recv.local.fullBytes(); - perf->byteRcvDrop = m_stats.recvDrop.local.fullBytes(); + perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(); + perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(); + perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(); - perf->pktSentUniqueTotal = m_stats.sent.total.pkts; - perf->pktRecvUniqueTotal = m_stats.recv.total.pkts; - perf->pktRcvDropTotal = m_stats.recvDrop.total.pkts; + perf->pktSentUniqueTotal = m_stats.sent.total.count(); + perf->pktRecvUniqueTotal = m_stats.recv.total.count(); + perf->pktRcvDropTotal = m_stats.recvDrop.total.count(); - perf->byteSentUniqueTotal = m_stats.sent.total.fullBytes(); - perf->byteRecvUniqueTotal = m_stats.recv.total.fullBytes(); - perf->byteRcvDropTotal = m_stats.recvDrop.total.fullBytes(); + perf->byteSentUniqueTotal = m_stats.sent.total.bytesWithHdr(); + perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(); + perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(); const double interval = static_cast(count_microseconds(currtime - m_stats.tsLastSampleTime)); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; @@ -3165,7 +3165,7 @@ CUDTGroup::BackupMemberState CUDTGroup::sendBackup_QualifyActiveState(const gli_ } enterCS(u.m_StatsLock); - const int64_t drop_total = u.m_stats.sndDropTotal; + const int64_t drop_total = u.m_stats.sndr.dropped.total.count(); leaveCS(u.m_StatsLock); const bool have_new_drops = d->pktSndDropTotal != drop_total; @@ -4039,7 +4039,7 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // At least one link has succeeded, update sending stats. - m_stats.sent.Update(len); + m_stats.sent.count(len); // Now fill in the socket table. Check if the size is enough, if not, // then set the pointer to NULL and set the correct size. diff --git a/srtcore/group.h b/srtcore/group.h index c61835e1b..99f1c0509 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -697,31 +697,29 @@ class CUDTGroup time_point tsActivateTime; // Time when this group sent or received the first data packet time_point tsLastSampleTime; // Time reset when clearing stats - MetricUsage sent; // number of packets sent from the application - MetricUsage recv; // number of packets delivered from the group to the application - MetricUsage - recvDrop; // number of packets dropped by the group receiver (not received from any member) - MetricUsage recvDiscard; // number of packets discarded as already delivered + stats::Metric sent; // number of packets sent from the application + stats::Metric recv; // number of packets delivered from the group to the application + stats::Metric recvDrop; // number of packets dropped by the group receiver (not received from any member) + stats::Metric recvDiscard; // number of packets discarded as already delivered void init() { tsActivateTime = srt::sync::steady_clock::time_point(); - sent.Init(); - recv.Init(); - recvDrop.Init(); - recvDiscard.Init(); - - reset(); + tsLastSampleTime = srt::sync::steady_clock::now(); + sent.reset(); + recv.reset(); + recvDrop.reset(); + recvDiscard.reset(); } void reset() { - sent.Clear(); - recv.Clear(); - recvDrop.Clear(); - recvDiscard.Clear(); - tsLastSampleTime = srt::sync::steady_clock::now(); + + sent.resetTrace(); + recv.resetTrace(); + recvDrop.resetTrace(); + recvDiscard.resetTrace(); } } m_stats; diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index dffb2a8ba..305af3547 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -138,8 +138,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo { // Packet not to be passthru, update stats ScopedLock lg(m_parent->m_StatsLock); - ++m_parent->m_stats.rcvFilterExtra; - ++m_parent->m_stats.rcvFilterExtraTotal; + m_parent->m_stats.rcvr.recvdFilterExtra.count(1); } // w_loss_seqs enters empty into this function and can be only filled here. XXX ASSERT? @@ -152,8 +151,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo if (dist > 0) { ScopedLock lg(m_parent->m_StatsLock); - m_parent->m_stats.rcvFilterLoss += dist; - m_parent->m_stats.rcvFilterLossTotal += dist; + m_parent->m_stats.rcvr.lossFilter.count(dist); } else { @@ -171,8 +169,7 @@ void srt::PacketFilter::receive(CUnit* unit, std::vector& w_incoming, lo InsertRebuilt(w_incoming, m_unitq); ScopedLock lg(m_parent->m_StatsLock); - m_parent->m_stats.rcvFilterSupply += nsupply; - m_parent->m_stats.rcvFilterSupplyTotal += nsupply; + m_parent->m_stats.rcvr.suppliedByFilter.count(nsupply); } // Now that all units have been filled as they should be, diff --git a/srtcore/stats.h b/srtcore/stats.h new file mode 100644 index 000000000..6d934b0a7 --- /dev/null +++ b/srtcore/stats.h @@ -0,0 +1,222 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2021 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef INC_SRT_STATS_H +#define INC_SRT_STATS_H + +#include "platform_sys.h" +#include "packet.h" + +namespace srt +{ +namespace stats +{ + +class Packets +{ +public: + Packets() : m_count(0) {} + + Packets(uint32_t num) : m_count(num) {} + + void reset() + { + m_count = 0; + } + + Packets& operator+= (const Packets& other) + { + m_count += other.m_count; + return *this; + } + + uint32_t count() const + { + return m_count; + } + +private: + uint32_t m_count; +}; + +class BytesPackets +{ +public: + BytesPackets() + : m_bytes(0) + , m_packets(0) + {} + + BytesPackets(uint64_t bytes, size_t n = 1) + : m_bytes(bytes) + , m_packets(n) + {} + + BytesPackets& operator+= (const BytesPackets& other) + { + m_bytes += other.m_bytes; + m_packets += other.m_packets; + return *this; + } + +public: + void reset() + { + m_packets = 0; + m_bytes = 0; + } + + void count(uint64_t bytes, size_t n = 1) + { + m_packets += (uint32_t) n; + m_bytes += bytes; + } + + uint64_t bytes() const + { + return m_bytes; + } + + uint32_t count() const + { + return m_packets; + } + + uint64_t bytesWithHdr() const + { + static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + return m_bytes + m_packets * PKT_HDR_SIZE; + } + +private: + uint64_t m_bytes; + uint32_t m_packets; +}; + +template +struct Metric +{ + METRIC_TYPE trace; + METRIC_TYPE total; + + void count(METRIC_TYPE val) + { + trace += val; + total += val; + } + + void reset() + { + trace.reset(); + total.reset(); + } + + void resetTrace() + { + trace.reset(); + } +}; + +/// Sender-side statistics. +struct Sender +{ + Metric sent; + Metric sentUnique; + Metric sentRetrans; // The number of data packets retransmitted by the sender. + Metric lost; // The number of packets reported lost (including repeated reports) to the sender in NAKs. + Metric dropped; // The number of data packets dropped by the sender. + + Metric sentFilterExtra; // The number of packets generate by the packet filter and sent by the sender. + + Metric recvdAck; // The number of ACK packets received by the sender. + Metric recvdNak; // The number of ACK packets received by the sender. + + void reset() + { + sent.reset(); + sentUnique.reset(); + sentRetrans.reset(); + lost.reset(); + dropped.reset(); + recvdAck.reset(); + recvdNak.reset(); + sentFilterExtra.reset(); + } + + void resetTrace() + { + sent.resetTrace(); + sentUnique.resetTrace(); + sentRetrans.resetTrace(); + lost.resetTrace(); + dropped.resetTrace(); + recvdAck.resetTrace(); + recvdNak.resetTrace(); + sentFilterExtra.resetTrace(); + } +}; + +/// Receiver-side statistics. +struct Receiver +{ + Metric recvd; + Metric recvdUnique; + Metric recvdRetrans; // The number of retransmitted data packets received by the receiver. + Metric lost; // The number of packets detected by the receiver as lost. + Metric dropped; // The number of packets dropped by the receiver (as too-late to be delivered). + Metric recvdBelated; // The number of belated packets received (dropped as too late but eventually received). + Metric undecrypted; // The number of packets received by the receiver that failed to be decrypted. + + Metric recvdFilterExtra; // The number of filter packets (e.g. FEC) received by the receiver. + Metric suppliedByFilter; // The number of lost packets got from the packet filter at the receiver side (e.g. loss recovered by FEC). + Metric lossFilter; // The number of lost DATA packets not recovered by the packet filter at the receiver side. + + Metric sentAck; // The number of ACK packets sent by the receiver. + Metric sentNak; // The number of NACK packets sent by the receiver. + + void reset() + { + recvd.reset(); + recvdUnique.reset(); + recvdRetrans.reset(); + lost.reset(); + dropped.reset(); + recvdBelated.reset(); + undecrypted.reset(); + recvdFilterExtra.reset(); + suppliedByFilter.reset(); + lossFilter.reset(); + sentAck.reset(); + sentNak.reset(); + } + + void resetTrace() + { + recvd.resetTrace(); + recvdUnique.resetTrace(); + recvdRetrans.resetTrace(); + lost.resetTrace(); + dropped.resetTrace(); + recvdBelated.resetTrace(); + undecrypted.resetTrace(); + recvdFilterExtra.resetTrace(); + suppliedByFilter.resetTrace(); + lossFilter.resetTrace(); + sentAck.resetTrace(); + sentNak.resetTrace(); + } +}; + +} // namespace stats +} // namespace srt + +#endif // INC_SRT_STATS_H + + From 1111cbd1f9212e0d7ef4fb8b26aa616a03754cb5 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 24 Dec 2021 12:56:24 +0100 Subject: [PATCH 218/683] [core] Fixed RCV TL drop of packets dropped by SND (#2214) Moved the TL drop logic from the TSBPD thread to a dedicated function. --- docs/API/statistics.md | 2 ++ srtcore/buffer_rcv.cpp | 9 ++++---- srtcore/buffer_rcv.h | 3 ++- srtcore/core.cpp | 51 ++++++++++++++++++++++++------------------ srtcore/core.h | 6 +++++ 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 334437bc7..7a3ee10db 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -235,6 +235,8 @@ where `SRTO_PEERLATENCY` is the configured SRT latency, `SRTO_SNDDROPDELAY` adds The total number of _dropped_ by the SRT receiver and, as a result, not delivered to the upstream application DATA packets (refer to [TLPKTDROP](https://github.com/Haivision/srt-rfc/blob/master/draft-sharabayko-mops-srt.md#too-late-packet-drop-too-late-packet-drop) mechanism). Available for receiver. This statistic counts + +- not arrived packets including those signalled for dropping by the sender, that were dropped in favor of the subsequent existing packets, - arrived too late packets (retransmitted or original packets arrived out of order), - arrived in time packets, but decrypted with errors (see also [pktRcvUndecryptTotal](#pktRcvUndecryptTotal) statistic). diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 0ef1d75de..014ccc0ea 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -159,7 +159,7 @@ int CRcvBufferNew::insert(CUnit* unit) return 0; } -void CRcvBufferNew::dropUpTo(int32_t seqno) +int CRcvBufferNew::dropUpTo(int32_t seqno) { // Can drop only when nothing to read, and // first unacknowledged packet is missing. @@ -173,17 +173,15 @@ void CRcvBufferNew::dropUpTo(int32_t seqno) if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); - return; + return 0; } - /*LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropUpTo(): seqno=" << seqno << ", pkts=" << len - << ". Buffer start " << m_iStartSeqNo << ".");*/ - m_iMaxPosInc -= len; if (m_iMaxPosInc < 0) m_iMaxPosInc = 0; // Check that all packets being dropped are missing. + const int iDropCnt = len; while (len > 0) { if (m_entries[m_iStartPos].pUnit != NULL) @@ -210,6 +208,7 @@ void CRcvBufferNew::dropUpTo(int32_t seqno) // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); + return iDropCnt; } void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 589cf2bc0..cee4329a6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -69,8 +69,9 @@ class CRcvBufferNew /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number + /// @return number of dropped packets. /// - void dropUpTo(int32_t seqno); + int dropUpTo(int32_t seqno); /// @brief Drop the whole message from the buffer. /// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi]. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 98b412f4e..986e76b4a 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5174,39 +5174,21 @@ void * srt::CUDT::tsbpd(void* param) rxready = true; if (info.seq_gap) { - const int seq_gap_len = CSeqNo::seqoff(self->m_iRcvLastSkipAck, info.seqno); - SRT_ASSERT(seq_gap_len > 0); - /*if (!info.seq_gap) - { - LOGC(brlog.Warn, log << "TSBPD worker: no gap. pktseqno=" << info.seqno - << ", m_iRcvLastSkipAck=" << self->m_iRcvLastSkipAck - << ", RBuffer start seqno=" << self->m_pRcvBuffer->getStartSeqNo() - << ", m_iRcvLastAck=" << self->m_iRcvLastAck - << ", init seqnoo=" << self->m_iISN); - }*/ - - // Drop too late packets - self->updateForgotten(seq_gap_len, self->m_iRcvLastSkipAck, info.seqno); - //if (info.seq_gap) // If there is no sequence gap, we are reading ahead of ACK. - //{ - self->m_pRcvBuffer->dropUpTo(info.seqno); - //} - - self->m_iRcvLastSkipAck = info.seqno; + const int iDropCnt SRT_ATR_UNUSED = self->dropTooLateUpTo(info.seqno); + #if ENABLE_EXPERIMENTAL_BONDING shall_update_group = true; #endif #if ENABLE_LOGGING const int64_t timediff_us = count_microseconds(tnow - info.tsbpd_time); - // TODO: seq_gap_len is not the actual number of packets dropped. #if ENABLE_HEAVY_LOGGING HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" - << seq_gap_len << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " + << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif - LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << seq_gap_len << " packet(s), packet seqno %" << info.seqno + LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); #endif @@ -5539,6 +5521,30 @@ void * srt::CUDT::tsbpd(void *param) } #endif // ENABLE_NEW_RCVBUFFER +int srt::CUDT::dropTooLateUpTo(int seqno) +{ + const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); + + // seq_gap_len can be <= 0 if a packet has been dropped by the sender. + if (seq_gap_len > 0) + { + // Remove [from,to-inclusive] + dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); + m_iRcvLastSkipAck = seqno; + } + + const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); + if (iDropCnt > 0) + { + enterCS(m_StatsLock); + // Estimate dropped bytes from average payload size. + const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (size_t) iDropCnt)); + leaveCS(m_StatsLock); + } + return iDropCnt; +} + void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { enterCS(m_StatsLock); @@ -7898,6 +7904,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // If there is no loss, the ACK is the current largest sequence number plus 1; // Otherwise it is the smallest sequence number in the receiver loss list. ScopedLock lock(m_RcvLossLock); + // TODO: Consider the Fresh Loss list as well!!! ack = m_pRcvLossList->getFirstLostSeq(); } diff --git a/srtcore/core.h b/srtcore/core.h index 08056fec7..8e3d7bc6d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -705,6 +705,12 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); + /// Drop too late packets. Updaet loss lists and ACK positions. + /// The @a seqno packet itself is not dropped. + /// @param seqno [in] The sequence number of the first packets following those to be dropped. + /// @return The number of packets dropped. + int dropTooLateUpTo(int seqno); + void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); From 3d26644e2b029b7da94713e1fd16e77006acc715 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Sun, 26 Dec 2021 11:44:17 +0100 Subject: [PATCH 219/683] [core] Fixed build with the old RCV buffer --- srtcore/core.cpp | 49 ++++++++++++++++++++++++------------------------ srtcore/core.h | 2 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 986e76b4a..6f5e17ff1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5302,6 +5302,31 @@ void * srt::CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); return NULL; } + +int srt::CUDT::dropTooLateUpTo(int seqno) +{ + const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); + + // seq_gap_len can be <= 0 if a packet has been dropped by the sender. + if (seq_gap_len > 0) + { + // Remove [from,to-inclusive] + dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); + m_iRcvLastSkipAck = seqno; + } + + const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); + if (iDropCnt > 0) + { + enterCS(m_StatsLock); + // Estimate dropped bytes from average payload size. + const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (size_t)iDropCnt)); + leaveCS(m_StatsLock); + } + return iDropCnt; +} + #else void * srt::CUDT::tsbpd(void *param) { @@ -5521,30 +5546,6 @@ void * srt::CUDT::tsbpd(void *param) } #endif // ENABLE_NEW_RCVBUFFER -int srt::CUDT::dropTooLateUpTo(int seqno) -{ - const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); - - // seq_gap_len can be <= 0 if a packet has been dropped by the sender. - if (seq_gap_len > 0) - { - // Remove [from,to-inclusive] - dropFromLossLists(m_iRcvLastSkipAck, CSeqNo::decseq(seqno)); - m_iRcvLastSkipAck = seqno; - } - - const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); - if (iDropCnt > 0) - { - enterCS(m_StatsLock); - // Estimate dropped bytes from average payload size. - const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (size_t) iDropCnt)); - leaveCS(m_StatsLock); - } - return iDropCnt; -} - void srt::CUDT::updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno) { enterCS(m_StatsLock); diff --git a/srtcore/core.h b/srtcore/core.h index 8e3d7bc6d..adf35b35b 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -705,11 +705,13 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); +#if ENABLE_NEW_RCVBUFFER /// Drop too late packets. Updaet loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. /// @return The number of packets dropped. int dropTooLateUpTo(int seqno); +#endif void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); From 258167de0500b3b26aa57da8f78cf025cd514fd2 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 12 Jan 2022 21:57:07 +0800 Subject: [PATCH 220/683] [core] replace ++ with incPos() in getTimespan_ms() --- srtcore/buffer_rcv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 014ccc0ea..6d783f0d3 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -507,7 +507,7 @@ int CRcvBufferNew::getTimespan_ms() const if (startpos == lastpos) break; - ++startpos; + startpos = incPos(startpos); } if (m_entries[startpos].pUnit == NULL) From 8afcdbeb4963ef3fe50f15d147359902fec38548 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 12 Jan 2022 17:48:22 +0800 Subject: [PATCH 221/683] [core] fix m_iMaxPosInc was not updated in releaseNextFillerEntries() --- srtcore/buffer_rcv.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 6d783f0d3..af3ab7f0c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -614,6 +614,9 @@ void CRcvBufferNew::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; + --m_iMaxPosInc; + if (m_iMaxPosInc < 0) + m_iMaxPosInc = 0; } } From 2aa90bb2bd89d93ea90ff2339af089e7396a6041 Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 17 Jan 2022 09:37:15 +0530 Subject: [PATCH 222/683] [build] CMake: fix system library names for MinGW (#2219) .lib extension is used in MSVC and MSVC-like toolchain. In MinGW environment, .dll.a extension is used for system import libraries. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d56c1200..55fecf541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -906,7 +906,7 @@ if (srt_libspec_shared) set_target_properties(${TARGET_srt}_shared PROPERTIES LINK_FLAGS "/DELAYLOAD:libeay32.dll") endif() elseif (MINGW) - target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32.lib ws2_32.lib) + target_link_libraries(${TARGET_srt}_shared PRIVATE wsock32 ws2_32) elseif (APPLE) set_property(TARGET ${TARGET_srt}_shared PROPERTY MACOSX_RPATH ON) endif() From 31de8aa56a4d276a5a608ae828bfb77271223de4 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Mon, 17 Jan 2022 20:53:08 +0800 Subject: [PATCH 223/683] [core] Add CRcvBufferNew::dropUnitInPos(..) (#2226) to simplify dropUpTo(..) and dropMessage(..) --- srtcore/buffer_rcv.cpp | 18 ++++++++++++++++++ srtcore/buffer_rcv.h | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index af3ab7f0c..a41e34329 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -605,6 +605,24 @@ void CRcvBufferNew::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } +bool CRcvBufferNew::dropUnitInPos(int pos) +{ + if (!m_entries[pos].pUnit) + return false; + if (m_tsbpd.isEnabled()) + { + updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + } + else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + { + --m_numOutOfOrderPackets; + if (pos == m_iFirstReadableOutOfOrder) + m_iFirstReadableOutOfOrder = -1; + } + releaseUnitInPos(pos); + return true; +} + void CRcvBufferNew::releaseNextFillerEntries() { int pos = m_iStartPos; diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index cee4329a6..0cba51972 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -221,6 +221,11 @@ class CRcvBufferNew void updateNonreadPos(); void releaseUnitInPos(int pos); + /// @brief Drop a unit from the buffer. + /// @param pos position in the m_entries of the unit to drop. + /// @return false if nothing to drop, true if the unit was dropped successfully. + bool dropUnitInPos(int pos); + /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). void releaseNextFillerEntries(); From 24bf666d1753026de7a18220f08183d667984778 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 Jan 2022 11:21:05 +0800 Subject: [PATCH 224/683] [core] CRcvBufferNew::dropUpTo() able to drop non-empty units (#2221) Using fixed TSBPD timebase update (#2226) when dropping. --- srtcore/buffer_rcv.cpp | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a41e34329..528bc1227 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -161,15 +161,10 @@ int CRcvBufferNew::insert(CUnit* unit) int CRcvBufferNew::dropUpTo(int32_t seqno) { - // Can drop only when nothing to read, and - // first unacknowledged packet is missing. - SRT_ASSERT(m_iStartPos == m_iFirstNonreadPos); - IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); - SRT_ASSERT(len > 0); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -180,21 +175,11 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) if (m_iMaxPosInc < 0) m_iMaxPosInc = 0; - // Check that all packets being dropped are missing. const int iDropCnt = len; while (len > 0) { - if (m_entries[m_iStartPos].pUnit != NULL) - { - releaseUnitInPos(m_iStartPos); - } - - if (m_entries[m_iStartPos].status != EntryState_Empty) - { - SRT_ASSERT(m_entries[m_iStartPos].status == EntryState_Drop || m_entries[m_iStartPos].status == EntryState_Read); - m_entries[m_iStartPos].status = EntryState_Empty; - } - + dropUnitInPos(m_iStartPos); + m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); m_iStartPos = incPos(m_iStartPos); --len; @@ -202,12 +187,14 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) // Update positions m_iStartSeqNo = seqno; - // Move forward if there are "read" entries. + // Move forward if there are "read/drop" entries. releaseNextFillerEntries(); // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + updateFirstReadableOutOfOrder(); return iDropCnt; } From 8c05c70d015c7d379823f389b5469901376ee5f4 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 18 Jan 2022 12:42:57 +0800 Subject: [PATCH 225/683] [core] Fix CRcvBufferNew::dropMessage() (#2222) (revise buffer state after drop) --- srtcore/buffer_rcv.cpp | 71 ++++++++++++++++++++++++++++++++++++++---- srtcore/buffer_rcv.h | 3 ++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 528bc1227..7e6e5a830 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -206,6 +206,7 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno != 0) { + int minDroppedOffset = -1; for (int i = m_iStartPos; i != end_pos; i = incPos(i)) { // TODO: Maybe check status? @@ -215,11 +216,26 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { - releaseUnitInPos(i); + dropUnitInPos(i); m_entries[i].status = EntryState_Drop; + if (minDroppedOffset == -1) + minDroppedOffset = offPos(m_iStartPos, i); } } - + // Check if units before m_iFirstNonreadPos are dropped. + bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); + releaseNextFillerEntries(); + if (needUpdateNonreadPos) + { + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); + } + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + { + if (!checkFirstReadableOutOfOrder()) + m_iFirstReadableOutOfOrder = -1; + updateFirstReadableOutOfOrder(); + } return; } @@ -235,17 +251,32 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int start_off = max(0, offset_a); const int last_pos = incPos(m_iStartPos, offset_b); + int minDroppedOffset = -1; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { - if (m_entries[i].pUnit) - { - releaseUnitInPos(i); - } + dropUnitInPos(i); m_entries[i].status = EntryState_Drop; + if (minDroppedOffset == -1) + minDroppedOffset = offPos(m_iStartPos, i); } LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): [" << seqnolo << "; " << seqnohi << "]."); + + // Check if units before m_iFirstNonreadPos are dropped. + bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); + releaseNextFillerEntries(); + if (needUpdateNonreadPos) + { + m_iFirstNonreadPos = m_iStartPos; + updateNonreadPos(); + } + if (!m_tsbpd.isEnabled() && m_bMessageAPI) + { + if (!checkFirstReadableOutOfOrder()) + m_iFirstReadableOutOfOrder = -1; + updateFirstReadableOutOfOrder(); + } } int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) @@ -720,6 +751,34 @@ void CRcvBufferNew::onInsertNotInOrderPacket(int insertPos) return; } +bool CRcvBufferNew::checkFirstReadableOutOfOrder() +{ + if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + return false; + + const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + int msgno = -1; + for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + { + if (!m_entries[pos].pUnit) + return false; + + const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + if (pkt.getMsgOrderFlag()) + return false; + + if (msgno == -1) + msgno = pkt.getMsgSeq(m_bPeerRexmitFlag); + else if (msgno != pkt.getMsgSeq(m_bPeerRexmitFlag)) + return false; + + if (pkt.getMsgBoundary() & PB_LAST) + return true; + } + + return false; +} + void CRcvBufferNew::updateFirstReadableOutOfOrder() { if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 0cba51972..f7e01d930 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -215,6 +215,7 @@ class CRcvBufferNew private: inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } + inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : (m_szSize + pos2 - pos1); } private: void countBytes(int pkts, int bytes); @@ -237,6 +238,8 @@ class CRcvBufferNew /// Scan for availability of out of order packets. void onInsertNotInOrderPacket(int insertpos); + // Check if m_iFirstReadableOutOfOrder is still readable. + bool checkFirstReadableOutOfOrder(); void updateFirstReadableOutOfOrder(); int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; From 85185585b6af006dcb31c0cd17040e5d141fe1fb Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Tue, 11 Jan 2022 17:13:42 +0800 Subject: [PATCH 226/683] [core] fixed missing m_RcvBufferLock in processCtrlDropReq() --- srtcore/core.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6f5e17ff1..4a5486b82 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8824,11 +8824,14 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { UniqueLock rlock(m_RecvLock); const bool using_rexmit_flag = m_bPeerRexmitFlag; + { + ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER - m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); #else - m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); + m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); #endif + } // When the drop request was received, it means that there are // packets for which there will never be ACK sent; if the TSBPD thread // is currently in the ACK-waiting state, it may never exit. From a31e618e93ffa80afe57dfef0ea7d590ac380c3a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 16:45:17 +0700 Subject: [PATCH 227/683] [core] Refactoring: added packUniqueData(..) func --- srtcore/buffer.cpp | 37 +++---- srtcore/core.cpp | 256 +++++++++++++++++++++---------------------- srtcore/core.h | 16 ++- srtcore/queue.cpp | 4 +- test/test_buffer.cpp | 2 +- 5 files changed, 154 insertions(+), 161 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index d07080981..4d95fe6ca 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -410,30 +410,19 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.setLength(readlen); w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; - // XXX This is probably done because the encryption should happen - // just once, and so this sets the encryption flags to both msgno bitset - // IN THE PACKET and IN THE BLOCK. This is probably to make the encryption - // happen at the time when scheduling a new packet to send, but the packet - // must remain in the send buffer until it's ACKed. For the case of rexmit - // the packet will be taken "as is" (that is, already encrypted). - // - // The problem is in the order of things: - // 0. When the application stores the data, some of the flags for PH_MSGNO are set. - // 1. The readData() is called to get the original data sent by the application. - // 2. The data are original and must be encrypted. They WILL BE encrypted, later. - // 3. So far we are in readData() so the encryption flags must be updated NOW because - // later we won't have access to the block's data. - // 4. After exiting from readData(), the packet is being encrypted. It's immediately - // sent, however the data must remain in the sending buffer until they are ACKed. - // 5. In case when rexmission is needed, the second overloaded version of readData - // is being called, and the buffer + PH_MSGNO value is extracted. All interesting - // flags must be present and correct at that time. - // - // The only sensible way to fix this problem is to encrypt the packet not after - // extracting from here, but when the packet is stored into CSndBuffer. The appropriate - // flags for PH_MSGNO will be applied directly there. Then here the value for setting - // PH_MSGNO will be set as is. - + // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). + // 2. The readData() is called to get the original (unique) payload not ever sent yet. + // The payload must be encrypted for the first time if the encryption + // is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet + // header must be set and remembered accordingly (see EncryptionKeySpec). + // 3. The next time this packet is read (only for retransmission), the payload is already + // encrypted, and the proper flag value is already stored. + + // TODO: Alternatively, encryption could happen before the packet is submitted to the buffer + // (before the addBuffer() call), and corresponding flags could be set accordingly. + // This may also put an encryption burden on the application thread, rather than the sending thread, + // which could be more efficient. Note that packet sequence number must be properly set in that case, + // as it is used as a counter for the AES encryption. if (kflgs == -1) { HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING."); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4a5486b82..483836599 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9189,7 +9189,7 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi return 0; } -std::pair srt::CUDT::packData(CPacket& w_packet) +std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; bool probe = false; @@ -9197,8 +9197,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) bool new_packet_packed = false; bool filter_ctl_pkt = false; - int kflg = EK_NOENC; - const steady_clock::time_point enter_time = steady_clock::now(); if (!is_zero(m_tsNextSendTime) && enter_time > m_tsNextSendTime) @@ -9217,7 +9215,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // start the dissolving process, this process will // not be started until this function is finished. if (!m_bOpened) - return std::make_pair(0, enter_time); + return std::make_pair(false, enter_time); payload = packLostData((w_packet), (origintime)); if (payload > 0) @@ -9233,122 +9231,24 @@ std::pair srt::CUDT::packData(CPacket& w_packet) filter_ctl_pkt = true; // Mark that this packet ALREADY HAS timestamp field and it should not be set // Stats - { - ScopedLock lg(m_StatsLock); - m_stats.sndr.sentFilterExtra.count(1); - } + ScopedLock lg(m_StatsLock); + m_stats.sndr.sentFilterExtra.count(1); } else { - // If no loss, and no packetfilter control packet, pack a new packet. - - // Check the congestion/flow window limit - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); - const int flightspan = getFlightSpan(); - if (cwnd > flightspan) - { - // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the - // send buffer. This should be somehow avoided, the crypto flags should be set - // together with encrypting, and the packet should be sent as is, when rexmitting. - // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field - // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. - kflg = m_pCryptoControl->getSndCryptoFlags(); - int pktskipseqno = 0; - payload = m_pSndBuffer->readData((w_packet), (origintime), kflg, (pktskipseqno)); - if (pktskipseqno) - { - // Some packets were skipped due to TTL expiry. - m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); - } - - if (payload) - { - // A CHANGE. The sequence number is currently added to the packet - // when scheduling, not when extracting. This is a inter-migration form, - // so still override the value, but trace it. - m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); - - // Do this checking only for groups and only at the very first moment, - // when there's still nothing in the buffer. Otherwise there will be - // a serious data discrepancy between the agent and the peer. - // After increasing by 1, but being previously set as ISN-1, this should be == ISN, - // if this is the very first packet to send. -#if ENABLE_EXPERIMENTAL_BONDING - // Fortunately here is only the procedure that verifies if the extraction - // sequence is moved due to the difference between ISN caught during the existing - // transmission and the first sequence possible to be used at the first sending - // instruction. The group itself isn't being accessed. - if (m_parent->m_GroupOf && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) - { - const int packetspan = CSeqNo::seqcmp(w_packet.m_iSeqNo, m_iSndCurrSeqNo); - - HLOGC(qslog.Debug, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo - << " DIFF: " << packetspan << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); - - // This is the very first packet to be sent; so there's nothing in - // the sending buffer yet, and therefore we are in a situation as just - // after connection. No packets in the buffer, no packets are sent, - // no ACK to be awaited. We can screw up all the variables that are - // initialized from ISN just after connection. - // - // Additionally send the drop request to the peer so that it - // won't stupidly request the packets to be retransmitted. - // Don't do it if the difference isn't positive or exceeds the threshold. - if (packetspan > 0) - { - int32_t seqpair[2]; - seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = w_packet.m_iSeqNo; - HLOGC(qslog.Debug, log << "... sending INITIAL DROP (ISN FIX): " - << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" - << seqpair[0] << " - " << seqpair[1] << "(" << packetspan << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); - - // In case when this message is lost, the peer will still get the - // UMSG_DROPREQ message when the agent realizes that the requested - // packet are not present in the buffer (preadte the send buffer). - } - } - else -#endif - { - HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo - << " DIFF: " << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) - << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); - -#if ENABLE_EXPERIMENTAL_BONDING - HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_GroupOf ? "yes":"no") - << " extraction-seq=" << m_iSndCurrSeqNo << " scheduling-seq=" << w_packet.m_iSeqNo << " ISN=" << m_iISN); -#endif - - // Do this always when not in a group, - w_packet.m_iSeqNo = m_iSndCurrSeqNo; - } - - // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) - probe = true; - - new_packet_packed = true; - } - else - { - m_tsNextSendTime = steady_clock::time_point(); - m_tdSendTimeDiff = steady_clock::duration(); - return std::make_pair(0, enter_time); - } - } - else + if (!packUniqueData(w_packet, origintime)) { - HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); m_tsNextSendTime = steady_clock::time_point(); m_tdSendTimeDiff = steady_clock::duration(); - return std::make_pair(0, enter_time); + return std::make_pair(false, enter_time); } + new_packet_packed = true; + + // every 16 (0xF) packets, a packet pair is sent + if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + probe = true; + payload = (int) w_packet.getLength(); reason = "normal"; } @@ -9389,23 +9289,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) w_packet.m_iID = m_PeerID; - /* Encrypt if 1st time this packet is sent and crypto is enabled */ - if (kflg) - { - // XXX Encryption flags are already set on the packet before calling this. - // See readData() above. - if (m_pCryptoControl->encrypt((w_packet))) - { - // Encryption failed - //>>Add stats for crypto failure - LOGC(qslog.Warn, log << "ENCRYPT FAILED - packet won't be sent, size=" << payload); - // Encryption failed - return std::make_pair(-1, enter_time); - } - payload = (int) w_packet.getLength(); /* Cipher may change length */ - reason += " (encrypted)"; - } - if (new_packet_packed && m_PacketFilter) { HLOGC(qslog.Debug, log << "filter: Feeding packet for source clip"); @@ -9472,7 +9355,120 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #endif } - return std::make_pair(payload, m_tsNextSendTime); + return std::make_pair(payload >= 0, m_tsNextSendTime); +} + +bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) +{ + // Check the congestion/flow window limit + const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int flightspan = getFlightSpan(); + if (cwnd <= flightspan) + { + HLOGC(qslog.Debug, log << "packData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); + return false; + } + + // XXX Here it's needed to set kflg to msgno_bitset in the block stored in the + // send buffer. This should be somehow avoided, the crypto flags should be set + // together with encrypting, and the packet should be sent as is, when rexmitting. + // It would be nice to research as to whether CSndBuffer::Block::m_iMsgNoBitset field + // isn't a useless redundant state copy. If it is, then taking the flags here can be removed. + const int kflg = m_pCryptoControl->getSndCryptoFlags(); + int pktskipseqno = 0; + const int pld_size = m_pSndBuffer->readData((w_packet), (w_origintime), kflg, (pktskipseqno)); + if (pktskipseqno) + { + // Some packets were skipped due to TTL expiry. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); + } + + if (pld_size == 0) + { + return false; + } + + // A CHANGE. The sequence number is currently added to the packet + // when scheduling, not when extracting. This is a inter-migration form, + // so still override the value, but trace it. + m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo); + + // Do this checking only for groups and only at the very first moment, + // when there's still nothing in the buffer. Otherwise there will be + // a serious data discrepancy between the agent and the peer. + // After increasing by 1, but being previously set as ISN-1, this should be == ISN, + // if this is the very first packet to send. +#if ENABLE_EXPERIMENTAL_BONDING + // Fortunately here is only the procedure that verifies if the extraction + // sequence is moved due to the difference between ISN caught during the existing + // transmission and the first sequence possible to be used at the first sending + // instruction. The group itself isn't being accessed. + if (m_parent->m_GroupOf && m_iSndCurrSeqNo != w_packet.m_iSeqNo && m_iSndCurrSeqNo == m_iISN) + { + const int packetspan = CSeqNo::seqcmp(w_packet.m_iSeqNo, m_iSndCurrSeqNo); + + HLOGC(qslog.Debug, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo + << " from SCHEDULING sequence " << w_packet.m_iSeqNo + << " DIFF: " << packetspan << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); + + // This is the very first packet to be sent; so there's nothing in + // the sending buffer yet, and therefore we are in a situation as just + // after connection. No packets in the buffer, no packets are sent, + // no ACK to be awaited. We can screw up all the variables that are + // initialized from ISN just after connection. + // + // Additionally send the drop request to the peer so that it + // won't stupidly request the packets to be retransmitted. + // Don't do it if the difference isn't positive or exceeds the threshold. + if (packetspan > 0) + { + int32_t seqpair[2]; + seqpair[0] = m_iSndCurrSeqNo; + seqpair[1] = w_packet.m_iSeqNo; + HLOGC(qslog.Debug, log << "... sending INITIAL DROP (ISN FIX): " + << "msg=" << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" + << seqpair[0] << " - " << seqpair[1] << "(" << packetspan << " packets)"); + sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + + // In case when this message is lost, the peer will still get the + // UMSG_DROPREQ message when the agent realizes that the requested + // packet are not present in the buffer (preadte the send buffer). + } + } + else +#endif + { + HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo + << " over SCHEDULING sequence " << w_packet.m_iSeqNo + << " DIFF: " << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " STAMP:" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); + +#if ENABLE_EXPERIMENTAL_BONDING + HLOGC(qslog.Debug, log << "... CONDITION: IN GROUP: " << (m_parent->m_GroupOf ? "yes":"no") + << " extraction-seq=" << m_iSndCurrSeqNo << " scheduling-seq=" << w_packet.m_iSeqNo << " ISN=" << m_iISN); +#endif + + // Do this always when not in a group, + w_packet.m_iSeqNo = m_iSndCurrSeqNo; + } + + // Encrypt if 1st time this packet is sent and crypto is enabled + if (kflg != EK_NOENC) + { + // Note that the packet header must have a valid seqno set, as it is used as a counter for encryption. + // Other fields of the data packet header (e.g. timestamp, destination socket ID) are not used for the counter. + // Cypher may change packet length! + if (m_pCryptoControl->encrypt((w_packet))) + { + // Encryption failed + //>>Add stats for crypto failure + LOGC(qslog.Warn, log << "ENCRYPT FAILED - packet won't be sent, size=" << pld_size); + return -1; + } + } + + return true; } // This is a close request, but called from the diff --git a/srtcore/core.h b/srtcore/core.h index adf35b35b..c25e9c8c1 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1030,15 +1030,23 @@ class CUDT /// @return payload size on success, <=0 on failure int packLostData(CPacket &packet, time_point &origintime); + /// Pack a unique data packet (never sent so far) in CPacket for sending. + /// + /// @param packet [in, out] a CPacket structure to fill. + /// @param origintime [in, out] origin timestamp of the packet. + /// + /// @return true if a packet has been packets; false otherwise. + bool packUniqueData(CPacket& packet, time_point& origintime); + /// Pack in CPacket the next data to be send. /// /// @param packet [in, out] a CPacket structure to fill /// - /// @return A pair of values is returned (payload, timestamp). - /// The payload tells the size of the payload, packed in CPacket. + /// @return A pair of values is returned (is_payload_valid, timestamp). + /// If is_payload_valid is false, there was nothing packed for sending, + /// and the timestamp value should be ignored. /// The timestamp is the full source/origin timestamp of the data. - /// If payload is <= 0, consider the timestamp value invalid. - std::pair packData(CPacket& packet); + std::pair packData(CPacket& packet); int processData(CUnit* unit); void processClose(); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 62d158af7..1e57b3021 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -635,10 +635,10 @@ void* srt::CSndQueue::worker(void* param) // pack a packet from the socket CPacket pkt; - const std::pair res_time = u->packData((pkt)); + const std::pair res_time = u->packData((pkt)); // Check if payload size is invalid. - if (res_time.first <= 0) + if (res_time.first == false) { #if defined(SRT_DEBUG_SNDQ_HIGHRATE) self->m_WorkerStats.lNotReadyPop++; diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index 2ba636525..edef007b6 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -465,7 +465,7 @@ TEST_F(CRcvBufferReadMsg, SmallReadBuffer) } // BUG!!! -// Checks signalling of read-readiness of a half-acknowledged message. +// Checks signaling of read-readiness of a half-acknowledged message. // The RCV buffer implementation has an issue here: when only half of the message is // acknowledged, the RCV buffer signals read-readiness, even though // the message can't be read, and reading returns 0. From 5773901bf2db9f05f2ecb4aa3557caa4558e25d8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 18:00:25 +0700 Subject: [PATCH 228/683] [core] Use SND buffer delay for TL Packet Drop instead of the timespan between the first and the last packet in the buffer. --- srtcore/buffer.cpp | 8 ++++++-- srtcore/buffer.h | 8 +++++--- srtcore/core.cpp | 25 +++++++++++-------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 4d95fe6ca..cbcbcf228 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -675,10 +675,14 @@ int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) return m_iCount; } -CSndBuffer::time_point CSndBuffer::getOldestTime() const +CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const { + ScopedLock lck(m_BufLock); SRT_ASSERT(m_pFirstBlock); - return m_pFirstBlock->m_tsOriginTime; + if (m_iCount == 0) + return duration(0); + + return tnow - m_pFirstBlock->m_tsOriginTime; } int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time) diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 6478e30b9..175ca19c0 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -179,7 +179,9 @@ class CSndBuffer int getAvgBufSize(int& bytes, int& timespan); int getCurrBufSize(int& bytes, int& timespan); - time_point getOldestTime() const; + /// @brief Get the buffering delay of the oldest message in the buffer. + /// @return the delay value. + duration getBufferingDelay(const time_point& tnow) const; uint64_t getInRatePeriod() const { return m_InRatePeriod; } @@ -207,7 +209,7 @@ class CSndBuffer static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE; private: - sync::Mutex m_BufLock; // used to synchronize buffer operation + mutable sync::Mutex m_BufLock; // used to synchronize buffer operation struct Block { @@ -216,7 +218,7 @@ class CSndBuffer int32_t m_iMsgNoBitset; // message number int32_t m_iSeqNo; // sequence number for scheduling - time_point m_tsOriginTime; // block origin time (either provided from above or equials the time a message was submitted for sending. + time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending. time_point m_tsRexmitTime; // packet retransmission time int m_iTTL; // time to live (milliseconds) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 483836599..2aece2d3d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6338,9 +6338,8 @@ bool srt::CUDT::checkNeedDrop() throw CUDTException(MJ_NOTSUP, MN_INVALBUFFERAPI, 0); } - int bytes, timespan_ms; - // (returns buffer size in buffer units, ignored) - m_pSndBuffer->getCurrBufSize((bytes), (timespan_ms)); + const time_point tnow = steady_clock::now(); + const int buffdelay_ms = count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow)); // high threshold (msec) at tsbpd_delay plus sender/receiver reaction time (2 * 10ms) // Minimum value must accomodate an I-Frame (~8 x average frame size) @@ -6348,21 +6347,19 @@ bool srt::CUDT::checkNeedDrop() // >>using 1 sec for worse case 1 frame using all bit budget. // picture rate would be useful in auto SRT setting for min latency // XXX Make SRT_TLPKTDROP_MINTHRESHOLD_MS option-configurable - int threshold_ms = 0; - if (m_config.iSndDropDelay >= 0) - { - threshold_ms = std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + - (2 * COMM_SYN_INTERVAL_US / 1000); - } + const int threshold_ms = (m_config.iSndDropDelay >= 0) + ? std::max(m_iPeerTsbPdDelay_ms + m_config.iSndDropDelay, +SRT_TLPKTDROP_MINTHRESHOLD_MS) + + (2 * COMM_SYN_INTERVAL_US / 1000) + : 0; bool bCongestion = false; - if (threshold_ms && timespan_ms > threshold_ms) + if (threshold_ms && buffdelay_ms > threshold_ms) { // protect packet retransmission enterCS(m_RecvAckLock); int dbytes; int32_t first_msgno; - int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), steady_clock::now() - milliseconds_from(threshold_ms)); + int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); if (dpkts > 0) { enterCS(m_StatsLock); @@ -6385,7 +6382,7 @@ bool srt::CUDT::checkNeedDrop() } HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" - << dpkts << "pkt " << dbytes << "B, span=" << timespan_ms << " ms, FIRST #" << first_msgno); + << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); #if ENABLE_EXPERIMENTAL_BONDING // This is done with a presumption that the group @@ -6413,10 +6410,10 @@ bool srt::CUDT::checkNeedDrop() bCongestion = true; leaveCS(m_RecvAckLock); } - else if (timespan_ms > (m_iPeerTsbPdDelay_ms / 2)) + else if (buffdelay_ms > (m_iPeerTsbPdDelay_ms / 2)) { HLOGC(aslog.Debug, - log << "cong, BYTES " << bytes << ", TMSPAN " << timespan_ms << "ms"); + log << "cong TIMESPAN " << buffdelay_ms << "ms"); bCongestion = true; } From 308cd309246745fca438b960fb6f6764f32fa1c4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 21 Jan 2022 18:11:20 +0700 Subject: [PATCH 229/683] [core] Added missing lock to CSndBuffer::readData --- srtcore/buffer.cpp | 1 + srtcore/buffer.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index cbcbcf228..1cd983be2 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -402,6 +402,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int readlen = 0; w_seqnoinc = 0; + ScopedLock bufferguard(m_BufLock); while (m_pCurrBlock != m_pLastBlock) { // Make the packet REFLECT the data stored in the buffer. diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 175ca19c0..693858fe3 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -133,12 +133,14 @@ class CSndBuffer /// @param [in] data pointer to the user data block. /// @param [in] len size of the block. /// @param [inout] w_mctrl Message control data + SRT_ATTR_EXCLUDES(m_BufLock) void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl); /// Read a block of data from file and insert it into the sending list. /// @param [in] ifs input file stream. /// @param [in] len size of the block. /// @return actual size of data added from the file. + SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); /// Find data position to pack a DATA packet from the furthest reading point. @@ -147,6 +149,7 @@ class CSndBuffer /// @param [in] kflags Odd|Even crypto key flag /// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented. /// @return Actual length of data read. + SRT_ATTR_EXCLUDES(m_BufLock) int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); /// Find data position to pack a DATA packet for a retransmission. @@ -155,12 +158,14 @@ class CSndBuffer /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + SRT_ATTR_EXCLUDES(m_BufLock) int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) /// /// @return Last time of the last retransmission event for the corresponding DATA packet. + SRT_ATTR_EXCLUDES(m_BufLock) time_point getPacketRexmitTime(const int offset); /// Update the ACK point and may release/unmap/return the user data according to the flag. @@ -173,6 +178,7 @@ class CSndBuffer /// @return Current size of the data in the sending list. int getCurrBufSize() const; + SRT_ATTR_EXCLUDES(m_BufLock) int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); void updAvgBufSize(const time_point& time); @@ -181,6 +187,7 @@ class CSndBuffer /// @brief Get the buffering delay of the oldest message in the buffer. /// @return the delay value. + SRT_ATTR_EXCLUDES(m_BufLock) duration getBufferingDelay(const time_point& tnow) const; uint64_t getInRatePeriod() const { return m_InRatePeriod; } From 912463b9111e9265e58490f22b5237d8cecdb38b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 24 Jan 2022 11:46:08 +0700 Subject: [PATCH 230/683] [core] Fix MaxBW limitation. Don't reschedule sending (keep pacing) on - SND drop, - NAK received - retransmission timeout. --- srtcore/congctl.cpp | 2 +- srtcore/core.cpp | 136 +++++++++++++++++++++----------------------- srtcore/core.h | 9 ++- srtcore/stats.h | 3 +- 4 files changed, 72 insertions(+), 78 deletions(-) diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index b394f8183..33b5389e0 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -77,7 +77,7 @@ class LiveCC: public SrtCongestionControlBase { m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE m_zMaxPayloadSize = parent->OPT_PayloadSize(); - if ( m_zMaxPayloadSize == 0 ) + if (m_zMaxPayloadSize == 0) m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2aece2d3d..62da98d45 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5174,8 +5174,7 @@ void * srt::CUDT::tsbpd(void* param) rxready = true; if (info.seq_gap) { - const int iDropCnt SRT_ATR_UNUSED = self->dropTooLateUpTo(info.seqno); - + const int iDropCnt SRT_ATR_UNUSED = self->rcvDropTooLateUpTo(info.seqno); #if ENABLE_EXPERIMENTAL_BONDING shall_update_group = true; #endif @@ -5303,7 +5302,7 @@ void * srt::CUDT::tsbpd(void* param) return NULL; } -int srt::CUDT::dropTooLateUpTo(int seqno) +int srt::CUDT::rcvDropTooLateUpTo(int seqno) { const int seq_gap_len = CSeqNo::seqoff(m_iRcvLastSkipAck, seqno); @@ -6327,10 +6326,10 @@ int srt::CUDT::receiveBuffer(char *data, int len) // [[using maybe_locked(CUDTGroup::m_GroupLock, m_parent->m_GroupOf != NULL)]]; // [[using locked(m_SendLock)]]; -bool srt::CUDT::checkNeedDrop() +int srt::CUDT::sndDropTooLate() { if (!m_bPeerTLPktDrop) - return false; + return 0; if (!m_config.bMessageAPI) { @@ -6352,72 +6351,64 @@ bool srt::CUDT::checkNeedDrop() + (2 * COMM_SYN_INTERVAL_US / 1000) : 0; - bool bCongestion = false; - if (threshold_ms && buffdelay_ms > threshold_ms) - { - // protect packet retransmission - enterCS(m_RecvAckLock); - int dbytes; - int32_t first_msgno; - int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); - if (dpkts > 0) - { - enterCS(m_StatsLock); - m_stats.sndr.dropped.count(stats::BytesPackets(dbytes, dpkts)); - leaveCS(m_StatsLock); + if (threshold_ms == 0 || buffdelay_ms <= threshold_ms) + return 0; - IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); - const int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts); + // protect packet retransmission + ScopedLock rcvlck(m_RecvAckLock); + int dbytes; + int32_t first_msgno; + const int dpkts = m_pSndBuffer->dropLateData((dbytes), (first_msgno), tnow - milliseconds_from(threshold_ms)); + if (dpkts <= 0) + return 0; - m_iSndLastAck = fakeack; - m_iSndLastDataAck = fakeack; + // If some packets were dropped update stats, socket state, loss list and the parent group if any. + enterCS(m_StatsLock); + m_stats.sndr.dropped.count(dbytes);; + leaveCS(m_StatsLock); - int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck); - m_pSndLossList->removeUpTo(minlastack); - /* If we dropped packets not yet sent, advance current position */ - // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1) - if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0) - { - m_iSndCurrSeqNo = minlastack; - } + IF_HEAVY_LOGGING(const int32_t realack = m_iSndLastDataAck); + const int32_t fakeack = CSeqNo::incseq(m_iSndLastDataAck, dpkts); - HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" - << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); + m_iSndLastAck = fakeack; + m_iSndLastDataAck = fakeack; -#if ENABLE_EXPERIMENTAL_BONDING - // This is done with a presumption that the group - // exists and if this is not NULL, it means that this - // function was called with locked m_GroupLock, as sendmsg2 - // function was called from inside CUDTGroup::send, which - // locks the whole function. - // - // XXX This is true only because all existing groups are managed - // groups, that is, sockets cannot be added or removed from group - // manually, nor can send/recv operation be done on a single socket - // from the API call directly. This should be extra verified, if that - // changes in the future. - // - if (m_parent->m_GroupOf) - { - // What's important is that the lock on GroupLock cannot be applied - // here, both because it might be applied already, that is, according - // to the condition defined at this function's header, it is applied - // under this condition. Hence ackMessage can be defined as 100% locked. - m_parent->m_GroupOf->ackMessage(first_msgno); - } -#endif - } - bCongestion = true; - leaveCS(m_RecvAckLock); - } - else if (buffdelay_ms > (m_iPeerTsbPdDelay_ms / 2)) + const int32_t minlastack = CSeqNo::decseq(m_iSndLastDataAck); + m_pSndLossList->removeUpTo(minlastack); + /* If we dropped packets not yet sent, advance current position */ + // THIS MEANS: m_iSndCurrSeqNo = MAX(m_iSndCurrSeqNo, m_iSndLastDataAck-1) + if (CSeqNo::seqcmp(m_iSndCurrSeqNo, minlastack) < 0) { - HLOGC(aslog.Debug, - log << "cong TIMESPAN " << buffdelay_ms << "ms"); + m_iSndCurrSeqNo = minlastack; + } - bCongestion = true; + HLOGC(aslog.Debug, log << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" + << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); + +#if ENABLE_EXPERIMENTAL_BONDING + // This is done with a presumption that the group + // exists and if this is not NULL, it means that this + // function was called with locked m_GroupLock, as sendmsg2 + // function was called from inside CUDTGroup::send, which + // locks the whole function. + // + // XXX This is true only because all existing groups are managed + // groups, that is, sockets cannot be added or removed from group + // manually, nor can send/recv operation be done on a single socket + // from the API call directly. This should be extra verified, if that + // changes in the future. + // + if (m_parent->m_GroupOf) + { + // What's important is that the lock on GroupLock cannot be applied + // here, both because it might be applied already, that is, according + // to the condition defined at this function's header, it is applied + // under this condition. Hence ackMessage can be defined as 100% locked. + m_parent->m_GroupOf->ackMessage(first_msgno); } - return bCongestion; +#endif + + return dpkts; } int srt::CUDT::sendmsg(const char *data, int len, int msttl, bool inorder, int64_t srctime) @@ -6520,9 +6511,9 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) m_iReXmitCount = 1; } - // checkNeedDrop(...) may lock m_RecvAckLock + // sndDropTooLate(...) may lock m_RecvAckLock // to modify m_pSndBuffer and m_pSndLossList - const bool bCongestion = checkNeedDrop(); + const int iPktsTLDropped SRT_ATR_UNUSED = sndDropTooLate(); int minlen = 1; // Minimum sender buffer space required for STREAM API if (m_config.bMessageAPI) @@ -6701,12 +6692,13 @@ int srt::CUDT::sendmsg2(const char *data, int len, SRT_MSGCTRL& w_mctrl) } } - // insert this socket to the snd list if it is not on the list yet + // Insert this socket to the snd list if it is not on the list already. // m_pSndUList->pop may lock CSndUList::m_ListLock and then m_RecvAckLock - m_pSndQueue->m_pSndUList->update(this, CSndUList::rescheduleIf(bCongestion)); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); #ifdef SRT_ENABLE_ECN - if (bCongestion) + // IF there was a packet drop on the sender side, report congestion to the app. + if (iPktsTLDropped > 0) { LOGC(aslog.Error, log << "sendmsg2: CONGESTION; reporting error"); throw CUDTException(MJ_AGAIN, MN_CONGESTION, 0); @@ -8192,7 +8184,7 @@ void srt::CUDT::updateSndLossListOnACK(int32_t ackdata_seqno) // Guard access to m_iSndAckedMsgNo field // Note: This can't be done inside CUDTGroup::ackMessage - // because this function is also called from CUDT::checkNeedDrop + // because this function is also called from CUDT::sndDropTooLate // called from CUDT::sendmsg2 called from CUDTGroup::send, which // applies the lock on m_GroupLock already. ScopedLock glk (*m_parent->m_GroupOf->exp_groupLock()); @@ -8696,7 +8688,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) } // the lost packet (retransmission) should be sent out immediately - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); enterCS(m_StatsLock); m_stats.sndr.recvdNak.count(1); @@ -11166,8 +11158,8 @@ void srt::CUDT::checkRexmitTimer(const steady_clock::time_point& currtime) const ECheckTimerStage stage = is_fastrexmit ? TEV_CHT_FASTREXMIT : TEV_CHT_REXMIT; updateCC(TEV_CHECKTIMER, EventVariant(stage)); - // immediately restart transmission - m_pSndQueue->m_pSndUList->update(this, CSndUList::DO_RESCHEDULE); + // schedule sending if not scheduled already + m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); } void srt::CUDT::checkTimers() diff --git a/srtcore/core.h b/srtcore/core.h index c25e9c8c1..caf1f9837 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -543,7 +543,10 @@ class CUDT void updateIdleLinkFrom(CUDT* source); - bool checkNeedDrop(); + /// @brief Drop packets too late to be delivered if any. + /// @returns the number of packets actually dropped. + SRT_ATTR_REQUIRES(m_RecvAckLock, m_StatsLock) + int sndDropTooLate(); /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, @@ -706,11 +709,11 @@ class CUDT static void* tsbpd(void* param); #if ENABLE_NEW_RCVBUFFER - /// Drop too late packets. Updaet loss lists and ACK positions. + /// Drop too late packets (receiver side). Updaet loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. /// @return The number of packets dropped. - int dropTooLateUpTo(int seqno); + int rcvDropTooLateUpTo(int seqno); #endif void updateForgotten(int seqlen, int32_t lastack, int32_t skiptoseqno); diff --git a/srtcore/stats.h b/srtcore/stats.h index 6d934b0a7..10523f851 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -91,8 +91,7 @@ class BytesPackets uint64_t bytesWithHdr() const { - static const int PKT_HDR_SIZE = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; - return m_bytes + m_packets * PKT_HDR_SIZE; + return m_bytes + m_packets * CPacket::SRT_DATA_HDR_SIZE; } private: From ef11d26cd0c8b0cc139960e8669d77817a6d7279 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 25 Jan 2022 15:45:42 +0700 Subject: [PATCH 231/683] [core] SND pacing: amendment on probing packets --- srtcore/core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 62da98d45..d893c671d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9313,10 +9313,13 @@ std::pair srt::CUDT::packData(CPacket& w_packet) m_stats.sndr.sentUnique.count(payload); leaveCS(m_StatsLock); + const duration sendint = m_tdSendInterval; if (probe) { // sends out probing packet pair m_tsNextSendTime = enter_time; + // Sending earlier, need to adjust the pace later on. + m_tdSendTimeDiff = m_tdSendTimeDiff.load() - sendint; probe = false; } else @@ -9324,7 +9327,6 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if USE_BUSY_WAITING m_tsNextSendTime = enter_time + m_tdSendInterval.load(); #else - const duration sendint = m_tdSendInterval; const duration sendbrw = m_tdSendTimeDiff; if (sendbrw >= sendint) From f372356bd5ed124a6f02e1d202c2deca99fe3eef Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 27 Jan 2022 10:37:12 +0100 Subject: [PATCH 232/683] [tests] Use std::random_device in Transmission.FileUpload --- test/test_file_transmission.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 8150ca3f2..5a646fb7d 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -18,9 +18,11 @@ #include "srt.h" +#include #include #include #include +#include #include //#pragma comment (lib, "ws2_32.lib") @@ -35,7 +37,7 @@ TEST(Transmission, FileUpload) SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); - int tt = SRTT_FILE; + const int tt = SRTT_FILE; srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); @@ -75,11 +77,13 @@ TEST(Transmission, FileUpload) std::ofstream outfile("file.source", std::ios::out | std::ios::binary); ASSERT_EQ(!!outfile, true) << srt_getlasterror_str(); - srand(time(0)); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); for (size_t i = 0; i < filesize; ++i) { - char outbyte = rand() % 255; + char outbyte = dis(mtrd); outfile.write(&outbyte, 1); } } From 08e6482fe6814d6c4a6929e101ed9874f08593f6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 27 Jan 2022 16:06:19 +0700 Subject: [PATCH 233/683] [core] SND prioritize original packets in live configuration --- srtcore/buffer.cpp | 9 +++ srtcore/buffer.h | 5 ++ srtcore/core.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++- srtcore/core.h | 6 ++ 4 files changed, 155 insertions(+), 1 deletion(-) diff --git a/srtcore/buffer.cpp b/srtcore/buffer.cpp index 1cd983be2..febd12f58 100644 --- a/srtcore/buffer.cpp +++ b/srtcore/buffer.cpp @@ -455,6 +455,15 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, return readlen; } +CSndBuffer::time_point CSndBuffer::peekNextOriginal() const +{ + ScopedLock bufferguard(m_BufLock); + if (m_pCurrBlock == m_pLastBlock) + return time_point(); + + return m_pCurrBlock->m_tsOriginTime; +} + int32_t CSndBuffer::getMsgNoAt(const int offset) { ScopedLock bufferguard(m_BufLock); diff --git a/srtcore/buffer.h b/srtcore/buffer.h index 693858fe3..80a2354ff 100644 --- a/srtcore/buffer.h +++ b/srtcore/buffer.h @@ -152,6 +152,11 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc); + /// Peek an information on the next original data packet to send. + /// @return origin time stamp of the next packet; epoch start time otherwise. + SRT_ATTR_EXCLUDES(m_BufLock) + time_point peekNextOriginal() const; + /// Find data position to pack a DATA packet for a retransmission. /// @param [in] offset offset from the last ACK point (backward sequence number difference) /// @param [out] packet the packet to read. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d893c671d..6f228c138 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9178,6 +9178,131 @@ int srt::CUDT::packLostData(CPacket& w_packet, steady_clock::time_point& w_origi return 0; } +#if SRT_DEBUG_TRACE_SND +class snd_logger +{ + typedef srt::sync::steady_clock steady_clock; + +public: + snd_logger() {} + + ~snd_logger() + { + ScopedLock lck(m_mtx); + m_fout.close(); + } + + struct + { + typedef srt::sync::steady_clock steady_clock; + long long usElapsed; + steady_clock::time_point tsNow; + int usSRTT; + int usRTTVar; + int msSndBuffSpan; + int msTimespanTh; + int msNextUniqueToSend; + long long usElapsedLastDrop; + bool canRexmit; + int iPktSeqno; + bool isRetransmitted; + } state; + + void trace() + { + using namespace srt::sync; + ScopedLock lck(m_mtx); + create_file(); + + m_fout << state.usElapsed << ","; + m_fout << state.usSRTT << ","; + m_fout << state.usRTTVar << ","; + m_fout << state.msSndBuffSpan << ","; + m_fout << state.msTimespanTh << ","; + m_fout << state.msNextUniqueToSend << ","; + m_fout << state.usElapsedLastDrop << ","; + m_fout << state.canRexmit << ","; + m_fout << state.iPktSeqno << ','; + m_fout << state.isRetransmitted << '\n'; + + m_fout.flush(); + } + +private: + void print_header() + { + m_fout << "usElapsed,usSRTT,usRTTVar,msSndBuffTimespan,msTimespanTh,msNextUniqueToSend,usDLastDrop,canRexmit,sndPktSeqno,isRexmit"; + m_fout << "\n"; + } + + void create_file() + { + if (m_fout.is_open()) + return; + + m_start_time = srt::sync::steady_clock::now(); + std::string str_tnow = srt::sync::FormatTimeSys(m_start_time); + str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part + while (str_tnow.find(':') != std::string::npos) + { + str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); + } + const std::string fname = "snd_trace_" + str_tnow + ".csv"; + m_fout.open(fname, std::ofstream::out); + if (!m_fout) + std::cerr << "IPE: Failed to open " << fname << "!!!\n"; + + print_header(); + } + +private: + srt::sync::Mutex m_mtx; + std::ofstream m_fout; + srt::sync::steady_clock::time_point m_start_time; +}; + +snd_logger g_snd_logger; +#endif // SRT_DEBUG_TRACE_SND + +bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) +{ + // Prioritization of original packets only applies to Live CC. + if (!m_bPeerTLPktDrop || !m_config.bMessageAPI) + return true; + + // TODO: lock sender buffer? + const time_point tsNextPacket = m_pSndBuffer->peekNextOriginal(); + +#if SRT_DEBUG_TRACE_SND + const int buffdelay_ms = count_milliseconds(m_pSndBuffer->getBufferingDelay(tnow)); + // If there is a small loss, still better to retransmit. If timespan is already big, + // then consider sending original packets. + const int threshold_ms_min = (2 * m_iSRTT + 4 * m_iRTTVar + COMM_SYN_INTERVAL_US) / 1000; + const int msNextUniqueToSend = count_milliseconds(tnow - tsNextPacket) + m_iPeerTsbPdDelay_ms; + + g_snd_logger.state.tsNow = tnow; + g_snd_logger.state.usElapsed = count_microseconds(tnow - m_stats.tsStartTime); + g_snd_logger.state.usSRTT = m_iSRTT; + g_snd_logger.state.usRTTVar = m_iRTTVar; + g_snd_logger.state.msSndBuffSpan = buffdelay_ms; + g_snd_logger.state.msTimespanTh = threshold_ms_min; + g_snd_logger.state.msNextUniqueToSend = msNextUniqueToSend; + g_snd_logger.state.usElapsedLastDrop = count_microseconds(tnow - m_tsLastTLDrop); + g_snd_logger.state.canRexmit = false; +#endif + + if (tsNextPacket != time_point()) + { + // Can send original packet, so just send it + return false; + } + +#if SRT_DEBUG_TRACE_SND + g_snd_logger.state.canRexmit = true; +#endif + return true; +} + std::pair srt::CUDT::packData(CPacket& w_packet) { int payload = 0; @@ -9206,7 +9331,10 @@ std::pair srt::CUDT::packData(CPacket& w_packet) if (!m_bOpened) return std::make_pair(false, enter_time); - payload = packLostData((w_packet), (origintime)); + payload = isRetransmissionAllowed(enter_time) + ? packLostData((w_packet), (origintime)) + : 0; + if (payload > 0) { reason = "reXmit"; @@ -9459,6 +9587,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet, time_point& w_origintime) } } +#if SRT_DEBUG_TRACE_SND + g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); + g_snd_logger.trace(); +#endif + return true; } diff --git a/srtcore/core.h b/srtcore/core.h index caf1f9837..c660f6732 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -548,6 +548,12 @@ class CUDT SRT_ATTR_REQUIRES(m_RecvAckLock, m_StatsLock) int sndDropTooLate(); + /// @bried Allow packet retransmission. + /// Depending on the configuration mode (live / file), retransmission + /// can be blocked if e.g. there are original packets pending to be sent. + /// @return true if retransmission is allowed; false otherwise. + bool isRetransmissionAllowed(const time_point& tnow); + /// Connect to a UDT entity as per hs request. This will update /// required data in the entity, then update them also in the hs structure, /// and then send the response back to the caller. From 409d3635d02925b66e03fc78310f1cc746778327 Mon Sep 17 00:00:00 2001 From: Maria Sharabayko Date: Tue, 1 Feb 2022 15:49:36 +0700 Subject: [PATCH 234/683] [core] Improved the condition for smoothed_rtt recalculation, bidirectional transmission --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6f228c138..afcb65126 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8382,7 +8382,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // TODO: The case of bidirectional transmission requires further // improvements and testing. Double smoothing is applied here to be // consistent with the previous behavior. - if (rtt != INITIAL_RTT && rttvar != INITIAL_RTTVAR) + if (rtt != INITIAL_RTT || rttvar != INITIAL_RTTVAR) { int iSRTT = m_iSRTT.load(), iRTTVar = m_iRTTVar.load(); iRTTVar = avg_iir<4>(iRTTVar, abs(rtt - iSRTT)); From 5b7ac45de5415ded11d493409368a936a0a169b2 Mon Sep 17 00:00:00 2001 From: Alain Kalker Date: Mon, 7 Feb 2022 09:20:34 +0100 Subject: [PATCH 235/683] [docs] srt-live-transmit.md: add ffplay, ffprobe examples (#2242) --- docs/apps/srt-live-transmit.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/apps/srt-live-transmit.md b/docs/apps/srt-live-transmit.md index 258a5bb02..e0c86bb9d 100644 --- a/docs/apps/srt-live-transmit.md +++ b/docs/apps/srt-live-transmit.md @@ -62,6 +62,16 @@ You should see the stream connect in `srt-live-transmit`. Now you can test in VLC (make sure you're using the latest version!) - just go to file -> open network stream and enter `srt://127.0.0.1:4201` and you should see bars and tone right away. +Or you can test using ffplay or ffprobe to inspect the stream: + +```shell +ffplay srt://127.0.0.1:4201 +``` +-or- +```shell +ffprobe srt://127.0.0.1:4201 +``` + If you're having trouble, make sure this works, then add complexity one step at a time (multicast, push vs listen, etc.). ## URI Syntax From ac854f262ac2a78f152005876cec0da593845166 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 20 Jan 2022 09:56:13 +0700 Subject: [PATCH 236/683] [core] Fixed setting the peer rexmit flag on the RCV buffer --- srtcore/buffer_rcv.cpp | 4 ++-- srtcore/buffer_rcv.h | 6 ++++-- srtcore/core.cpp | 20 ++++++++++---------- srtcore/core.h | 1 + test/test_buffer.cpp | 3 ++- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7e6e5a830..dd32f518c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -70,7 +70,7 @@ namespace { * m_iMaxPosInc: none? (modified on add and ack */ -CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI) +CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) @@ -81,7 +81,7 @@ CRcvBufferNew::CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, , m_iNotch(0) , m_numOutOfOrderPackets(0) , m_iFirstReadableOutOfOrder(-1) - , m_bPeerRexmitFlag(peerRexmit) + , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index f7e01d930..0ad8d4245 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -51,7 +51,7 @@ class CRcvBufferNew typedef sync::steady_clock::duration duration; public: - CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool peerRexmit, bool bMessageAPI); + CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI); ~CRcvBufferNew(); @@ -308,7 +308,7 @@ class CRcvBufferNew size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to // read - const bool m_bPeerRexmitFlag; // Needed to read message number correctly + bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. public: // TSBPD public functions @@ -320,6 +320,8 @@ class CRcvBufferNew /// @return 0 void setTsbPdMode(const time_point& timebase, bool wrap, duration delay); + void setPeerRexmitFlag(bool flag) { m_bPeerRexmitFlag = flag; } + void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift); void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index afcb65126..8c696cd4e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2414,7 +2414,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } // We still believe it should work, let's check the flags. - int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType); + const int ext_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(hs.m_iType); if (ext_flags == 0) { m_RejectReason = SRT_REJ_ROGUE; @@ -4019,15 +4019,16 @@ EConnectStatus srt::CUDT::processRendezvous( m_ConnReq.m_iReqType = rsp_type; m_ConnReq.m_extension = needs_extension; - // This must be done before prepareConnectionObjects(). + // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers. if (!applyResponseSettings()) { LOGC(cnlog.Error, log << "processRendezvous: rogue peer"); return CONN_REJECT; } - // This must be done before interpreting and creating HSv5 extensions. - if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0)) + // The CryptoControl must be created by the prepareConnectionObjects() before interpreting and creating HSv5 extensions + // because the it will be used there. + if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, NULL)) { // m_RejectReason already handled HLOGC(cnlog.Debug, log << "processRendezvous: rejecting due to problems in prepareConnectionObjects."); @@ -4536,6 +4537,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // however in this case the HSREQ extension will not be attached, // so it will simply go the "old way". // (&&: skip if failed already) + // Must be called before interpretSrtHandshake() to create the CryptoControl. ok = ok && prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout); // May happen that 'response' contains a data packet that was sent in rendezvous mode. @@ -5568,11 +5570,8 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd return true; } - bool bidirectional = false; - if (hs.m_iVersion > HS_VERSION_UDT4) - { - bidirectional = true; // HSv5 is always bidirectional - } + // HSv5 is always bidirectional + const bool bidirectional = (hs.m_iVersion > HS_VERSION_UDT4); // HSD_DRAW is received only if this side is listener. // If this side is caller with HSv5, HSD_INITIATOR should be passed. @@ -5595,7 +5594,7 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize); #if ENABLE_NEW_RCVBUFFER SRT_ASSERT(m_iISN != -1); - m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_bPeerRexmitFlag, m_config.bMessageAPI); + m_pRcvBuffer = new srt::CRcvBufferNew(m_iISN, m_config.iRcvBufSize, &(m_pRcvQueue->m_UnitQueue), m_config.bMessageAPI); #else m_pRcvBuffer = new CRcvBuffer(&(m_pRcvQueue->m_UnitQueue), m_config.iRcvBufSize); #endif @@ -8982,6 +8981,7 @@ void srt::CUDT::updateSrtRcvSettings() enterCS(m_RecvLock); #if ENABLE_NEW_RCVBUFFER m_pRcvBuffer->setTsbPdMode(m_tsRcvPeerStartTime, false, milliseconds_from(m_iTsbPdDelay_ms)); + m_pRcvBuffer->setPeerRexmitFlag(m_bPeerRexmitFlag); #else m_pRcvBuffer->setRcvTsbPdMode(m_tsRcvPeerStartTime, milliseconds_from(m_iTsbPdDelay_ms)); #endif diff --git a/srtcore/core.h b/srtcore/core.h index c660f6732..62027b026 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -489,6 +489,7 @@ class CUDT SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus processRendezvous(const CPacket* response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt); + /// Create the CryptoControl object based on the HS packet. Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout); diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp index edef007b6..287297a00 100644 --- a/test/test_buffer.cpp +++ b/test/test_buffer.cpp @@ -34,7 +34,8 @@ class CRcvBufferReadMsg #if ENABLE_NEW_RCVBUFFER const bool enable_msg_api = m_use_message_api; const bool enable_peer_rexmit = true; - m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_peer_rexmit, enable_msg_api)); + m_rcv_buffer = unique_ptr(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_msg_api)); + m_rcv_buffer->setPeerRexmitFlag(enable_peer_rexmit); #else m_rcv_buffer = unique_ptr(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts)); #endif From 8f68f613c5546aca16b301ab0dceae6db28bb6c3 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Wed, 8 Dec 2021 21:08:39 +0800 Subject: [PATCH 237/683] [core] refactor Group::recv() base on new rcv buffer to support message mode --- srtcore/buffer_rcv.cpp | 47 +++++++++-- srtcore/buffer_rcv.h | 2 + srtcore/group.cpp | 188 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 5 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index dd32f518c..8ac7e7a81 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -292,8 +292,6 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; - // Remember if we actually read out of order packet. - const bool readingOutOfOrderPacket = !canReadInOrder || m_iStartPos == m_iFirstReadableOutOfOrder; size_t remain = len; char* dst = data; @@ -331,13 +329,14 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) { - msgctrl->pktseq = pktseqno; msgctrl->msgno = packet.getMsgSeq(m_bPeerRexmitFlag); } if (msgctrl && pbLast) { msgctrl->srctime = count_microseconds(getPktTsbPdTime(packet.getMsgTimeStamp()).time_since_epoch()); } + if (msgctrl) + msgctrl->pktseq = pktseqno; releaseUnitInPos(i); if (updateStartPos) @@ -362,8 +361,6 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) } countBytes(-pkts_read, -bytes_extracted); - if (!m_tsbpd.isEnabled() && readingOutOfOrderPacket) - updateFirstReadableOutOfOrder(); releaseNextFillerEntries(); @@ -373,6 +370,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) //updateNonreadPos(); } + if (!m_tsbpd.isEnabled()) + // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // incase readable inorder packets are all read out. + updateFirstReadableOutOfOrder(); + const int bytes_read = dst - data; if (bytes_read < bytes_extracted) { @@ -606,6 +608,41 @@ bool CRcvBufferNew::isRcvDataReady(time_point time_now) const return info.tsbpd_time <= time_now; } +CRcvBufferNew::PacketInfo CRcvBufferNew::getFirstReadablePacketInfo(time_point time_now) const +{ + const PacketInfo unreadableInfo = {SRT_SEQNO_NONE, false, time_point()}; + const bool hasInorderPackets = hasReadableInorderPkts(); + + if (!m_tsbpd.isEnabled()) + { + if (hasInorderPackets) + { + const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const PacketInfo info = {packet.getSeqNo(), false, time_point()}; + return info; + } + SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstReadableOutOfOrder >= 0) + { + SRT_ASSERT(m_numOutOfOrderPackets > 0); + const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + const PacketInfo info = {packet.getSeqNo(), true, time_point()}; + return info; + } + return unreadableInfo; + } + + if (!hasInorderPackets) + return unreadableInfo; + + const PacketInfo info = getFirstValidPacketInfo(); + + if (info.tsbpd_time <= time_now) + return info; + else + return unreadableInfo; +} + void CRcvBufferNew::countBytes(int pkts, int bytes) { ScopedLock lock(m_BytesCountLock); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 0ad8d4245..c21b037c3 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -164,6 +164,8 @@ class CRcvBufferNew /// IF skipseqno == -1, no missing packet but 1st not ready to play. PacketInfo getFirstValidPacketInfo() const; + PacketInfo getFirstReadablePacketInfo(time_point time_now) const; + /// Get information on packets available to be read. /// @returns a pair of sequence numbers (first available; first unavailable). /// diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b9c18090a..c1ba15d75 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2195,6 +2195,193 @@ static bool isValidSeqno(int32_t iBaseSeqno, int32_t iPktSeqno) return false; } +#ifdef ENABLE_NEW_RCVBUFFER +int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) +{ + // First, acquire GlobControlLock to make sure all member sockets still exist + enterCS(m_Global.m_GlobControlLock); + ScopedLock guard(m_GroupLock); + + if (m_bClosing) + { + // The group could be set closing in the meantime, but if + // this is only about to be set by another thread, this thread + // must fist wait for being able to acquire this lock. + // The group will not be deleted now because it is added usage counter + // by this call, but will be released once it exits. + leaveCS(m_Global.m_GlobControlLock); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + // Now, still under lock, check if all sockets still can be dispatched + send_CheckValidSockets(); + leaveCS(m_Global.m_GlobControlLock); + + if (m_bClosing) + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + + // Later iteration over it might be less efficient than + // by vector, but we'll also often try to check a single id + // if it was ever seen broken, so that it's skipped. + set broken; + + for (;;) + { + if (!m_bOpened || !m_bConnected) + { + LOGC(grlog.Error, + log << boolalpha << "grp/recv: $" << id() << ": ABANDONING: opened=" << m_bOpened + << " connected=" << m_bConnected); + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } + + vector aliveMembers; + recv_CollectAliveAndBroken(aliveMembers, broken); + if (aliveMembers.empty()) + { + LOGC(grlog.Error, log << "grp/recv: ALL LINKS BROKEN, ABANDONING."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); + } + + vector readySockets; + if (m_bSynRecving) + readySockets = recv_WaitForReadReady(aliveMembers, broken); + else + readySockets = aliveMembers; + + if (m_bClosing) + { + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": GROUP CLOSED, ABANDONING."); + throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); + } + + // Find the first readable packet among all member sockets. + CUDTSocket* socketToRead = NULL; + CRcvBufferNew::PacketInfo infoToRead = {-1, false, time_point()}; + for (vector::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) + { + CUDTSocket* ps = *si; + + ScopedLock lg(ps->core().m_RcvBufferLock); + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + // Drop here to make sure the getFirstReadablePacketInfo() below return fresher packet. + int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + if (cnt > 0) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": dropped " << cnt + << " packets before reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo); + } + } + + const CRcvBufferNew::PacketInfo info = + ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": Nothing to read."); + continue; + } + // We need to qualify the sequence, just for a case. + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && !isValidSeqno(m_RcvBaseSeqNo, info.seqno)) + { + LOGC(grlog.Error, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" + << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + ps->core().m_bBroken = true; + broken.insert(ps); + continue; + } + if (socketToRead == NULL || CSeqNo::seqcmp(info.seqno, infoToRead.seqno) < 0) + { + socketToRead = ps; + infoToRead = info; + } + } + + if (socketToRead == NULL) + { + if (m_bSynRecving) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": No links reported any fresher packet, re-polling."); + continue; + } + else + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": No links reported any fresher packet, clearing readiness."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); + } + } + else + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": Found first readable packet from @" << socketToRead->m_SocketID + << ": seq=" << infoToRead.seqno << " gap=" << infoToRead.seq_gap + << " time=" << FormatTime(infoToRead.tsbpd_time)); + } + + const int res = socketToRead->core().receiveMessage((buf), len, (w_mc), CUDTUnited::ERH_RETURN); + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": Extracted data with %" + << w_mc.pktseq << " #" << w_mc.msgno << ": " << (res <= 0 ? "(NOTHING)" : BufferStamp(buf, res))); + if (res == 0) + { + LOGC(grlog.Warn, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": Retrying next socket..."); + // This socket will not be socketToRead in the next turn because receiveMessage() return 0 here. + continue; + } + if (res == SRT_ERROR) + { + LOGC(grlog.Warn, + log << "grp/recv: $" << id() << ": @" << socketToRead->m_SocketID << ": " << srt_getlasterror_str() + << ". Retrying next socket..."); + broken.insert(socketToRead); + continue; + } + fillGroupData((w_mc), w_mc); + + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": Update m_RcvBaseSeqNo: %" << m_RcvBaseSeqNo << " -> %" << w_mc.pktseq); + m_RcvBaseSeqNo = w_mc.pktseq; + + // Update stats as per delivery + m_stats.recv.count(res); + updateAvgPayloadSize(res); + + for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) + { + CUDTSocket* ps = *si; + ScopedLock lg(ps->core().m_RcvBufferLock); + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + if (cnt > 0) + { + HLOGC(grlog.Debug, + log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": dropped " << cnt + << " packets after reading: m_RcvBaseSeqNo=" << m_RcvBaseSeqNo); + } + } + } + for (vector::const_iterator si = aliveMembers.begin(); si != aliveMembers.end(); ++si) + { + CUDTSocket* ps = *si; + if (!ps->core().isRcvBufferReady()) + m_Global.m_EPoll.update_events(ps->m_SocketID, ps->core().m_sPollID, SRT_EPOLL_IN, false); + } + + return res; + } + LOGC(grlog.Error, log << "grp/recv: UNEXPECTED RUN PATH, ABANDONING."); + m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_IN, false); + throw CUDTException(MJ_AGAIN, MN_RDAVAIL, 0); +} +#else // The "app reader" version of the reading function. // This reads the packets from every socket treating them as independent // and prepared to work with the application. Then packets are sorted out @@ -2731,6 +2918,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } } } +#endif // [[using locked(m_GroupLock)]] CUDTGroup::ReadPos* CUDTGroup::checkPacketAhead() From 650dbe6d8231f3ffc14c16d9c08b4f51f67cac78 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 7 Feb 2022 15:11:54 +0100 Subject: [PATCH 238/683] [core] Fixed rcvDropTooLateUpTo calls. Broken after merging #2218 --- srtcore/group.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c1ba15d75..66a78321d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2267,7 +2267,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { // Drop here to make sure the getFirstReadablePacketInfo() below return fresher packet. - int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); if (cnt > 0) { HLOGC(grlog.Debug, @@ -2359,7 +2359,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - int cnt = ps->core().dropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); if (cnt > 0) { HLOGC(grlog.Debug, From c885ed152d8222800bd498167b90031de5878228 Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Thu, 6 Jan 2022 14:16:44 +0800 Subject: [PATCH 239/683] [core] Group::updateReadState() support out-of-order messages --- srtcore/core.cpp | 27 +++++++++++++-------------- srtcore/core.h | 3 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 8c696cd4e..29fe71fd0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7663,7 +7663,7 @@ void srt::CUDT::releaseSynch() } // [[using locked(m_RcvBufferLock)]]; -int32_t srt::CUDT::ackDataUpTo(int32_t ack) +void srt::CUDT::ackDataUpTo(int32_t ack) { const int acksize SRT_ATR_UNUSED = CSeqNo::seqoff(m_iRcvLastSkipAck, ack); @@ -7673,16 +7673,7 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) m_iRcvLastAck = ack; m_iRcvLastSkipAck = ack; -#if ENABLE_NEW_RCVBUFFER - const std::pair range = m_pRcvBuffer->getAvailablePacketsRange(); - // Some packets acknowledged are not available in the buffer. - if (CSeqNo::seqcmp(range.second, ack) < 0) - { - LOGC(xtlog.Error, log << "IPE: Acknowledged seqno %" << ack << " outruns the RCV buffer state %" << range.first - << " - %" << range.second); - } - return CSeqNo::decseq(range.second); -#else +#if !ENABLE_NEW_RCVBUFFER // NOTE: This is new towards UDT and prevents spurious // wakeup of select/epoll functions when no new packets // were signed off for extraction. @@ -7690,7 +7681,6 @@ int32_t srt::CUDT::ackDataUpTo(int32_t ack) { m_pRcvBuffer->ackData(acksize); } - return ack; #endif } @@ -7930,7 +7920,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // IF ack %> m_iRcvLastAck if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) { - const int32_t group_read_seq SRT_ATR_UNUSED = ackDataUpTo(ack); + ackDataUpTo(ack); + +#if ENABLE_EXPERIMENTAL_BONDING +#if ENABLE_NEW_RCVBUFFER + const int32_t group_read_seq = m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()).seqno; +#else + const int32_t group_read_seq = CSeqNo::decseq(ack); +#endif +#endif + InvertedLock un_bufflock (m_RcvBufferLock); #if ENABLE_EXPERIMENTAL_BONDING @@ -8015,7 +8014,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #if ENABLE_EXPERIMENTAL_BONDING - if (m_parent->m_GroupOf) + if (group_read_seq != SRT_SEQNO_NONE && m_parent->m_GroupOf) { // See above explanation for double-checking ScopedLock glock (uglobal().m_GlobControlLock); diff --git a/srtcore/core.h b/srtcore/core.h index 62027b026..4f87ad181 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1075,8 +1075,7 @@ class CUDT /// @brief Acknowledge reading position up to the @p seq. /// Updates m_iRcvLastAck and m_iRcvLastSkipAck to @p seq. /// @param seq first unacknowledged packet sequence number. - /// @return - int32_t ackDataUpTo(int32_t seq); + void ackDataUpTo(int32_t seq); void handleKeepalive(const char* data, size_t lenghth); From 0c5bf7a8e99a59b1e8fd5bebb888beaf9be19900 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 14 Feb 2022 15:21:39 +0100 Subject: [PATCH 240/683] [core] Decreased SND drop request log level to Debug --- srtcore/buffer_rcv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 8ac7e7a81..ce9437b51 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -244,8 +244,8 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); if (offset_b < 0) { - LOGC(rbuflog.Warn, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " + << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); return; } From 81a31da7d378e5ffcfabc8058ebefd1ee4337ecb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 18 Feb 2022 10:55:05 +0100 Subject: [PATCH 241/683] [core] Fix RCV drop count when dropping on SND DROP REQ. Extended RCVBUF trace logging. --- srtcore/buffer_rcv.cpp | 26 +++++++++++++++++++------- srtcore/buffer_rcv.h | 3 ++- srtcore/core.cpp | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ce9437b51..c05eb4e77 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -110,7 +110,9 @@ int CRcvBufferNew::insert(CUnit* unit) const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::insert: seqno " << seqno); + IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); + IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); if (offset < 0) { @@ -198,7 +200,7 @@ int CRcvBufferNew::dropUpTo(int32_t seqno) return iDropCnt; } -void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) +int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); @@ -206,7 +208,9 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno != 0) { + IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; + int iDropCnt = 0; for (int i = m_iStartPos; i != end_pos; i = incPos(i)) { // TODO: Maybe check status? @@ -216,12 +220,14 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { + ++iDropCnt; dropUnitInPos(i); m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); } } + IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); // Check if units before m_iFirstNonreadPos are dropped. bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); @@ -236,7 +242,7 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) m_iFirstReadableOutOfOrder = -1; updateFirstReadableOutOfOrder(); } - return; + return iDropCnt; } // Drop by packet seqno range. @@ -246,15 +252,17 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { LOGC(rbuflog.Debug, log << "CRcvBufferNew.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); - return; + return 0; } const int start_off = max(0, offset_a); const int last_pos = incPos(m_iStartPos, offset_b); int minDroppedOffset = -1; + int iDropCnt = 0; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { dropUnitInPos(i); + ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); @@ -277,6 +285,8 @@ void CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) m_iFirstReadableOutOfOrder = -1; updateFirstReadableOutOfOrder(); } + + return iDropCnt; } int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) @@ -288,11 +298,11 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) return 0; } - IF_RCVBUF_DEBUG(ScopedLog scoped_log); - IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo); - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + IF_RCVBUF_DEBUG(ScopedLog scoped_log); + IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBufferNew::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); + size_t remain = len; char* dst = data; int pkts_read = 0; @@ -381,6 +391,8 @@ int CRcvBufferNew::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) LOGC(rbuflog.Error, log << "readMessage: small dst buffer, copied only " << bytes_read << "/" << bytes_extracted << " bytes."); } + IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + return bytes_read; } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index c21b037c3..a5e113dfc 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -79,7 +79,8 @@ class CRcvBufferNew /// @param seqnolo sequence number of the first packet in the dropping range. /// @param seqnohi sequence number of the last packet in the dropping range. /// @param msgno message number to drop (0 if unknown) - void dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + /// @return the number of packets actually dropped. + int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); /// Read the whole message from one or several packets. /// diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 29fe71fd0..de4e6b548 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8814,7 +8814,20 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER - m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + + if (iDropCnt > 0) + { + LOGC(brlog.Warn, log << CONID() << "RCV-DROPPED " << iDropCnt << " packet(s), seqno range %" + << dropdata[0] << "-%" << dropdata[1] << ", msgno " << ctrlpkt.getMsgSeq(using_rexmit_flag) + << " (SND DROP REQUEST)."); + + enterCS(m_StatsLock); + // Estimate dropped bytes from average payload size. + const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (size_t)iDropCnt)); + leaveCS(m_StatsLock); + } #else m_pRcvBuffer->dropMsg(ctrlpkt.getMsgSeq(using_rexmit_flag), using_rexmit_flag); #endif From 5adc2db2da20eff7620100874444a28c5f656db0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 18 Feb 2022 11:00:20 +0100 Subject: [PATCH 242/683] [core] RCV don't drop packets on SND drop request if they already exist in the buffer and can be read (full message is available). --- srtcore/buffer_rcv.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index c05eb4e77..5fb4a5992 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -217,6 +217,7 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) if (!m_entries[i].pUnit) continue; + // TODO: Break the loop if a massege has been found. No need to search further. const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { @@ -261,6 +262,11 @@ int CRcvBufferNew::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) int iDropCnt = 0; for (int i = incPos(m_iStartPos, start_off); i != end_pos && i != last_pos; i = incPos(i)) { + // Don't drop messages, if all its packets are already in the buffer. + // TODO: Don't drop a several-packet message if all packets are in the buffer. + if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + continue; + dropUnitInPos(i); ++iDropCnt; m_entries[i].status = EntryState_Drop; From 7d77d417c24c3233340a94da1613512d19ba4194 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 21 Feb 2022 09:59:44 +0100 Subject: [PATCH 243/683] [core] SND Drop Request: ignore if TLPktDrop and TSBPD are enabled to reduce false drops when a packet can still arrive later. It will be dropped anyway as too late. --- srtcore/core.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index de4e6b548..c8b2ed8c6 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8810,8 +8810,13 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { UniqueLock rlock(m_RecvLock); - const bool using_rexmit_flag = m_bPeerRexmitFlag; + // With both TLPktDrop and TsbPd enabled, a message always consists only of one packet. + // It will be dropped as too late anyway. Not dropping it from the receiver buffer + // in advance reduces false drops if the packet somehow manages to arrive. + // Still remove the record from the loss list to cease further retransmission requests. + if (!m_bTLPktDrop || !m_bTsbPd) { + const bool using_rexmit_flag = m_bPeerRexmitFlag; ScopedLock rblock(m_RcvBufferLock); #if ENABLE_NEW_RCVBUFFER const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); @@ -8845,10 +8850,8 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) dropFromLossLists(dropdata[0], dropdata[1]); - // move forward with current recv seq no. - // SYMBOLIC: - // if (dropdata[0] <=% 1 +% m_iRcvCurrSeqNo - // && dropdata[1] >% m_iRcvCurrSeqNo ) + // If dropping ahead of the current largest sequence number, + // move the recv seq number forward. if ((CSeqNo::seqcmp(dropdata[0], CSeqNo::incseq(m_iRcvCurrSeqNo)) <= 0) && (CSeqNo::seqcmp(dropdata[1], m_iRcvCurrSeqNo) > 0)) { From 9b95ffc383f18a8d55aacf16038fa8638b0061ca Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 2 Mar 2022 15:00:33 +0100 Subject: [PATCH 244/683] [docs] Reworked the build options document. (#2247) * [docs] Reworked the build options document. Describe CMake build options of SRT, reference corresponding option of the configure script. Co-authored-by: stevomatthews --- docs/build/build-options.md | 523 +++++++++++++++++++++++++----------- 1 file changed, 367 insertions(+), 156 deletions(-) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index cdac4c068..325c05821 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -1,44 +1,149 @@ # Build System -The main build system for SRT is provided by the `cmake` tool, which can be -used directly. There's also a wrapper script named `configure` (requires Tcl -interpreter) that can make operating with the options easier. +The SRT build system uses [`CMake`](https://cmake.org/) 2.8.12 or above. + +A wrapper script named [`configure`](https://github.com/Haivision/srt/blob/master/configure) +is also available. The `configure` script can simplify the build process, such as +by trying to automatically detect the OpenSSL path in a system. Note that you must +have the Tcl interpreter installed to use this script. -## Portability +Here is a link to a demo showing how CMake can be used to build SRT: +[Quickstart: Running SRT and FFmpeg on Ubuntu](https://www.youtube.com/watch?v=XOtUOVhussc&t=5s) -The `cmake` build system was tested on the following platforms: - - Linux (various flavors) - - macOS (see [Building SRT for iOS](build-iOS.md)) - - Windows with MinGW - - Windows with Microsoft Visual Studio (see [Building SRT for Windows](build-win.md)) - - Android (see [Building SRT for Android](build-android.md)) - - Cygwin (only for testing) +Additional information on building with Windows is available in the +[Building SRT for Windows](https://github.com/Haivision/srt/blob/master/docs/build/build-win.md) +document and on the [SRT Cookbook web site](https://srtlab.github.io/srt-cookbook/getting-started/build-on-windows/). -The `configure` script wasn't tested on Windows (other than on Cygwin). +## List of Build Options -## The `configure` Script +The following table lists available build options in alphabetical order. +Option details are given further below. -This script is similar in design to the Autotools' `configure` script, and -so usually handles `--long-options`, possibly with values. It handles -two kinds of options: -* special options to be resolved inside the script and that may do some -advanced checks; this should later turn into a set of specific `cmake` -variable declarations +| Option Name | Since | Type | Default | Short Description | +| :----------------------------------------------------------- | :---: | :-------: | :--------: | ----------------- | +| [`CMAKE_INSTALL_PREFIX`](#cmake_install_prefix) | 1.3.0 | `STRING` | OFF | Standard CMake variable that establishes the root directory for installation, inside of which a GNU/POSIX compatible directory layout will be used. | +| [`CYGWIN_USE_POSIX`](#cygwin_use_posix) | 1.2.0 | `BOOL` | OFF | Determines when to compile on Cygwin using POSIX API. | +| [`ENABLE_APPS`](#enable_apps) | 1.3.3 | `BOOL` | ON | Enables compiling sample applications (srt-live-trasnmit, etc.). | +| [`ENABLE_CXX_DEPS`](#enable_cxx_deps) | 1.3.2 | `BOOL` | OFF | The `pkg-confg` file (`srt.pc`) will be generated with the `libstdc++` library as a dependency. | +| [`ENABLE_CXX11`](#enable_cxx11) | 1.2.0 | `BOOL` | ON | Enable compiling in C++11 mode for those parts that may require it. Default: ON except for GCC<4.7 | +| [`ENABLE_CODE_COVERAGE`](#enable_code_coverage) | 1.4.0 | `BOOL` | OFF | Enables instrumentation for code coverage. | +| [`ENABLE_DEBUG`](#enable_debug>) | 1.2.0 | `INT` | ON | Allows release/debug control through the `CMAKE_BUILD_TYPE` variable. | +| [`ENABLE_ENCRYPTION`](#enable_encryption) | 1.3.3 | `BOOL` | ON | Enables encryption feature enabled, with dependency on an external encryption library. | +| [`ENABLE_GETNAMEINFO`](#enable_getnameinfo) | 1.3.0 | `BOOL` | OFF | Enables the use of `getnameinfo` to allow using reverse DNS to resolve an internal IP address into a readable internet domain name. | +| [`ENABLE_HAICRYPT_LOGGING`](#enable_haicrypt_logging) | 1.3.1 | `BOOL` | OFF | Enables logging in the *haicrypt* module, which serves as a connector to an encryption library. | +| [`ENABLE_HEAVY_LOGGING`](#enable_heavy_logging) | 1.3.0 | `BOOL` | OFF | Enables heavy logging instructions in the code that occur often and cover many detailed aspects of library behavior. Default: OFF in release mode. | +| [`ENABLE_INET_PTON`](#enable_inet_pton) | 1.3.2 | `BOOL` | ON | Enables usage of the `inet_pton` function used to resolve the network endpoint name into an IP address. | +| [`ENABLE_LOGGING`](#enable_logging) | 1.2.0 | `BOOL` | ON | Enables normal logging, including errors. | +| [`ENABLE_MONOTONIC_CLOCK`](#enable_monotonic_clock) | 1.4.0 | `BOOL` | ON* | Enforces the use of `clock_gettime` with a monotonic clock that is independent of the currently set time in the system. | +| [`ENABLE_NEW_RCVBUFFER`](#enable_new_rcvbuffer) | 1.4.5 | `BOOL` | ON | Enables the new implementation of the receiver buffer with behavior and code improvements (in dev build 1.4.5 only). | +| [`ENABLE_PROFILE`](#enable_profile) | 1.2.0 | `BOOL` | OFF | Enables code instrumentation for profiling (only for GNU-compatible compilers). | +| [`ENABLE_RELATIVE_LIBPATH`](#enable_relative_libpath) | 1.3.2 | `BOOL` | OFF | Enables adding a relative path to a library for linking against a shared SRT library by reaching out to a sibling directory. | +| [`ENABLE_SHARED`](#enable_shared--enable_static) | 1.2.0 | `BOOL` | ON | Enables building SRT as a shared library | +| [`ENABLE_SHOW_PROJECT_CONFIG`](#enable_show_project_config) | 1.4.5 | `BOOL` | OFF | When ON, the project configuration is displayed at the end of the CMake Configuration Step (in dev build 1.4.5 only). | +| [`ENABLE_STATIC`](#enable_shared--enable_static) | 1.3.0 | `BOOL` | ON | Enables building SRT as a tatic library | +| [`ENABLE_STDCXX_SYNC`](#enable_stdcxx_sync) | 1.4.2 | `BOOL` | ON* | Enables the standard C++11 `thread` and `chrono` libraries to be used by SRT instead of the `pthreads`. | +| [`ENABLE_TESTING`](#enable_testing) | 1.3.0 | `BOOL` | OFF | Enables compiling of developer testing applications (srt-test-live, etc.). | +| [`ENABLE_THREAD_CHECK`](#enable_thread_check) | 1.3.0 | `BOOL` | OFF | Enables `#include `, which implements `THREAD_*` macros" to support better thread debugging. | +| [`ENABLE_UNITTESTS`](#enable_unittests) | 1.3.2 | `BOOL` | OFF | Enables building unit tests. | +| [`OPENSSL_CRYPTO_LIBRARY`](#openssl_crypto_library) | 1.3.0 | `STRING` | OFF | Configures the path to an OpenSSL Crypto library. | +| [`OPENSSL_INCLUDE_DIR`](#openssl_include_dir) | 1.3.0 | `STRING` | OFF | Configures the path to include files for an OpenSSL library. | +| [`OPENSSL_SSL_LIBRARY`](#openssl_ssl_library) | 1.3.0 | `STRING` | OFF | Configures the path to an OpenSSL SSL library. | +| [`PKG_CONFIG_EXECUTABLE`](#pkg_config_executable) | 1.3.0 | `BOOL` | OFF | Configures the path to the `pkg-config` tool. | +| [`PTHREAD_INCLUDE_DIR`](#pthread_include_dir) | 1.3.0 | `STRING` | OFF | Configures the path to include files for a pthread library. | +| [`PTHREAD_LIBRARY`](#pthread_library) | 1.3.0 | `STRING` | OFF | Configures the path to a pthread library. | +| [`USE_BUSY_WAITING`](#use_busy_waiting) | 1.3.3 | `BOOL` | OFF | Enables more accurate sending times at the cost of potentially higher CPU load. | +| [`USE_CXX_STD`](#use_cxx_std) | 1.4.2 | `STRING` | OFF | Enforces using a particular C++ standard (11, 14, 17, etc.) when compiling. | +| [`USE_ENCLIB`](#use_enclib) | 1.3.3 | `STRING` | openssl | Encryption library to be used (`openssl`, `gnutls`, `mbedtls`). | +| [`USE_GNUSTL`](#use_gnustl) | 1.3.4 | `BOOL` | OFF | Use `pkg-config` with the `gnustl` package name to extract the header and library path for the C++ standard library. | +| [`USE_OPENSSL_PC`](#use_openssl_pc) | 1.3.0 | `BOOL` | ON | Use `pkg-config` to find OpenSSL libraries. | +| [`USE_STATIC_LIBSTDCXX`](#use_static_libstdcxx) | 1.2.0 | `BOOL` | OFF | Enforces linking the SRT library against the static libstdc++ library. | +| [`WITH_COMPILER_PREFIX`](#with_compiler_prefix) | 1.3.0 | `STRING` | OFF | Sets C/C++ toolchains as `` and ``, overriding the default compiler. | +| [`WITH_COMPILER_TYPE`](#with_compiler_type) | 1.3.0 | `STRING` | OFF | Sets the compiler type to be used (values: gcc, cc, clang, etc.). | +| [`WITH_EXTRALIBS`](#with_extralibs) | 1.3.0 | `STRING` | OFF | Option required for unusual situations when a platform-specific workaround is needed and some extra libraries must be passed explicitly for linkage. | +| [`WITH_SRT_NAME`](#with_srt_name) | 1.3.0 | `STRING` | OFF | Configure the SRT library name adding a custom `` | +| | | | | | + + +\* See the option description for more details. + +## Using CMake + +If you choose to use CMake directly for the build configuration stage, you must +specify option values in the CMake format: + +`-D