From 6e0ab4e520a43518a8c3032217483bf8c78f2c72 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 24 Jan 2024 12:46:03 +0100 Subject: [PATCH 01/85] [core] Refax of the CSndBuffer::readData making the call form clearer (#2593). --- srtcore/buffer_snd.cpp | 50 +++++++++++++++++++++++++++--------------- srtcore/buffer_snd.h | 29 +++++++++++++++++++----- srtcore/core.cpp | 27 +++++++++++------------ 3 files changed, 69 insertions(+), 37 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 4edfe80c0..3aa81042e 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -422,9 +422,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, DropRange& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -439,19 +440,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -463,11 +468,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -477,18 +482,27 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[DropRange::BEGIN] = w_packet.m_iSeqNo; + w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[DropRange::END] == CSeqNo::decseq(p->m_iSeqNo)); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 1b1aa6222..afd52110b 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct DropRange + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + /// @param [in,out] w_packet storage for the packet, preinitialized with sequence number + /// @param [out] w_origintime origin time stamp of the message + /// @param [out] w_drop the drop information in case when dropping is to be done instead + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, DropRange& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index cd22ebece..dcb264993 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9335,28 +9335,27 @@ int srt::CUDT::packLostData(CPacket& w_packet) } } - int msglen; + typedef CSndBuffer::DropRange DropRange; + + DropRange buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[DropRange::BEGIN], buffer_drop.seqno[DropRange::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[DropRange::BEGIN] << " - " + << buffer_drop.seqno[DropRange::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[DropRange::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[DropRange::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored From 3dba3f441890b430380fba25848e8ff4a4db4bf3 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:56:57 +0100 Subject: [PATCH 02/85] [core] Removed the possibility to use optlen=-1 in srt_setsockopt (#2849). It was used to auto-determine the length of a null-terminated string for the SRTO_BINDTODEVICE socket option. --- srtcore/api.cpp | 2 +- srtcore/socketconfig.cpp | 11 +++-------- test/test_socket_options.cpp | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7be0d1d95..554bed9fd 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3807,7 +3807,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { - if (!optval) + if (!optval || optlen < 0) return APIError(MJ_NOTSUP, MN_INVAL, 0); try diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 8708e90a1..d44330f78 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -292,10 +292,8 @@ struct CSrtConfigSetter using namespace std; string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); + + val.assign((const char *)optval, optlen); if (val.size() >= IFNAMSIZ) { LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); @@ -597,10 +595,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { std::string val; - if (optlen == -1) - val = (const char*)optval; - else - val.assign((const char*)optval, optlen); + val.assign((const char*)optval, optlen); // Translate alias if (val == "vod") diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index b7acda37a..78388bf30 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -850,6 +850,29 @@ TEST_F(TestSocketOptions, StreamIDWrongLen) EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); } +//Check if setting -1 as optlen returns an error +TEST_F(TestSocketOptions, StringOptLenInvalid) +{ + const string test_string = "test1234567"; + const string srto_congestion_string ="live"; + const string fec_config = "fec,cols:10,rows:10"; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_BINDTODEVICE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_CONGESTION, srto_congestion_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PACKETFILTER, fec_config.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PASSPHRASE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); +} + // Try to set/get a 13-character string in SRTO_STREAMID. // This tests checks that the StreamID is set to the correct size // while it is transmitted as 16 characters in the Stream ID HS extension. From 4c443d63a9e2236a69d260cb5bc5a68f87bb20d0 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:50:00 +0100 Subject: [PATCH 03/85] [core] Moved declaration of SRT_ATR_ALIGNAS to fix a warning (#2866). --- srtcore/channel.h | 7 ------- srtcore/srt_attr_defs.h | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/srtcore/channel.h b/srtcore/channel.h index 6df7ec0ce..e09b13fd9 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -49,13 +49,6 @@ written by modified by Haivision Systems Inc. *****************************************************************************/ -#if HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) -#else -#define SRT_ATR_ALIGNAS(n) -#endif #ifndef INC_SRT_CHANNEL_H #define INC_SRT_CHANNEL_H diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 84daabeb1..85ea9f96d 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -31,6 +31,14 @@ used by SRT library internally. #define ATR_DEPRECATED #endif +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#else +#define SRT_ATR_ALIGNAS(n) +#endif + #if defined(__cplusplus) && __cplusplus > 199711L #define HAVE_CXX11 1 // For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, From 8b73dbc4fcbf3ba8f07883317f1c4d092b3bd8d9 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Feb 2024 13:01:01 +0100 Subject: [PATCH 04/85] [core] Fixed the RCV buff nonread position update condition in case of dropping upto a sequence number. --- srtcore/buffer_rcv.cpp | 2 +- srtcore/buffer_rcv.h | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7ea0945bc..fb389e4be 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -239,7 +239,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (CSeqNo::seqcmp(m_iFirstNonreadPos, m_iStartPos) < 0) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index d4b50fab7..d664373f5 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -239,9 +239,15 @@ class CRcvBuffer inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + + /// @brief Compares the two positions in the receiver buffer relative to the starting position. + /// @param pos2 a position in the receiver buffer. + /// @param pos1 a position in the receiver buffer. + /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. inline int cmpPos(int pos2, int pos1) const { - // XXX maybe not the best implementation, but this keeps up to the rule + // XXX maybe not the best implementation, but this keeps up to the rule. + // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; From faefc98ada024d92a10312a0028c13614d95eda7 Mon Sep 17 00:00:00 2001 From: kura <86100095+kura979@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:47:06 +0900 Subject: [PATCH 05/85] [core] Fixed RCV loss list inserting a seqno with a big distance after the largest seqno (#2877). --- srtcore/list.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 0a175ee9d..0fe7d2b4b 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -653,6 +653,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } @@ -708,6 +710,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } From 0e215cdcdbb0924a9053c82376f90eac5cd7b069 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 10:50:23 +0100 Subject: [PATCH 06/85] [docs] Updated the documentation about latency and transtype (#2875). Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 38 ++++-- docs/features/latency.md | 219 +++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 docs/features/latency.md diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index ef6f87513..84e361f6d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -301,7 +301,8 @@ connection is rejected - **however** you may also change the value of this option for the accepted socket in the listener callback (see `srt_listen_callback`) if an appropriate instruction was given in the Stream ID. -Currently supported congestion controllers are designated as "live" and "file" +Currently supported congestion controllers are designated as "live" and "file", +which correspond to the Live and File modes. Note that it is not recommended to change this option manually, but you should rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. @@ -772,7 +773,8 @@ for more details. | `SRTO_LATENCY` | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | This option sets both [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) and [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) -to the same value specified. +to the same value specified. Note that the default value for `SRTO_RCVLATENCY` is modified by the +[`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. Prior to SRT version 1.3.0 `SRTO_LATENCY` was the only option to set the latency. However it is effectively equivalent to setting `SRTO_PEERLATENCY` in the sending direction @@ -1212,6 +1214,8 @@ considered broken on timeout. The latency value (as described in [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY)) provided by the sender side as a minimum value for the receiver. +This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. + Reading the value of the option on an unconnected socket reports the configured value. Reading the value on a connected socket reports the effective receiver buffering latency of the peer. @@ -1296,16 +1300,22 @@ This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabl **Default value**: 120 ms in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). The latency value defines the **minimum** receiver buffering delay before delivering an SRT data packet -from a receiving SRT socket to a receiving application. The provided value is used in the connection establishment (handshake exchange) stage -to fix the end-to-end latency of the transmission. The effective end-to-end latency `L` will be fixed -as the network transmission time of the final handshake packet (~1/2 RTT) plus the **negotiated** latency value `Ln`. -Data packets will stay in the receiver buffer for at least `L` microseconds since the timestamp of the -packet, independent of the actual network transmission times (RTT variations) of these packets. +from a receiving SRT socket to a receiving application. The actual value of the receiver buffering delay `Ln` (the negotiated latency) used on a connection is determined by the negotiation in the connection establishment (handshake exchange) phase as the maximum of the `SRTO_RCVLATENCY` value and the value of [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) set by the peer. +The general idea for the latency mechanism is to keep the time distance between two consecutive +received packets the same as the time when these same packets were scheduled for sending by the +sender application (or per the time explicitly declared when sending - see +[`srt_sendmsg2`](API-functions.md#srt_sendmsg2) for details). This keeps any packets that have arrived +earlier than their delivery time in the receiver buffer until their delivery time comes. This should +compensate for any jitter in the network and provides an extra delay needed for a packet retransmission. + +For detailed information on how the latency setting influences the actual packet delivery time and +how this time is defined, refer to the [latency documentation](../features/latency.md). + Reading the `SRTO_RCVLATENCY` value on a socket after the connection is established provides the actual (negotiated) latency value `Ln`. @@ -1638,9 +1648,19 @@ enabled in sender if receiver supports it. Sets the transmission type for the socket, in particular, setting this option sets multiple other parameters to their default values as required for a -particular transmission type. +particular transmission type. This sets the following options to their defaults +in particular mode: + +* [`SRTO_CONGESTION`](#SRTO_CONGESTION) +* [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) +* [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) +* [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY), also set as [`SRTO_LATENCY`](#SRTO_LATENCY) +* [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) +* [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) + + -Values defined by enum `SRT_TRANSTYPE` (see above for possible values) +Values defined by enum [`SRT_TRANSTYPE`](#SRT_TRANSTYPE). [Return to list](#list-of-options) diff --git a/docs/features/latency.md b/docs/features/latency.md new file mode 100644 index 000000000..d287ac030 --- /dev/null +++ b/docs/features/latency.md @@ -0,0 +1,219 @@ +## General statement about latency + +In the live streaming there are many things happening between the +camera's lens and the screen of the video player, all of which contribute +to a delay that is generally referred to as "latency". This overall latency +includes the time it takes for the camera frame grabber device to pass +frames to the encoder, encoding, multiplexing, **sending over the network**, +splitting, decoding and then finally displaying. + +In SRT, however, "latency" is defined as only the delay introduced by **sending +over the network**. It's the time between the moment when the `srt_sendmsg2` +function is called at the sender side up to the moment when the `srt_recvmsg2` +function is called at the receiver side. This SRT latency is the actual time difference +between these two events. + + +## The goal of the latency (TSBPD) mechanism + +SRT employs a TimeStamp Based Packet Delivery (TSBPD) mechanism +with strict goal of keeping the time interval between two consecutive packets +on the receiver side identical to what they were at the sender side. This +requires introducing an extra delay that should define when exactly the packet +can be retrieved by the receiver application -- if the packet arrives early, it must +wait in the receiver buffer until the delivery time. This time for a packet N +is roughly defined as: + +``` +PTS[N] = ETS[N] + LATENCY(option) +``` + +where `ETS[N]` is the time when the packet would arrive, if all delays +from the network and the processing software on both sides are identical +to what they were for the very first received data packet. This means that +for the very first packet `ETS[0]` is equal to this packet's arrival time. +For every following packet the delivery time interval should be equal to the +that packet's declared scheduling time interval. + + +## SRT's approach to packet arrival time + +SRT provides two socket options `SRTO_PEERLATENCY` and `SRTO_RCVLATENCY`. +While they have "latency" in their names, they do *not* define the true time +interval between the `srt_sendmsg2` and `srt_recvmsg2` calls for the same +packet. They are only used to add an extra delay (at the receiver side) to +the time when the packet "should" arrive (ETS). This extra delay is used to +compensate for two things: + +* an extra network delay (that is, if the packet arrived later than it +"should have arrived"), or + +* a packet retransmission. + +Note that many of the values included in these formulas are not controllable and +some cannot be measured directly. In many cases there are measured values +that are sums of other values, but the component values can't be extracted. + +There are two values that we can obtain at the receiver side: + +* ATS: actual arrival time, which is the time when the UDP packet +has been extracted through the `recvmsg` system call. + +* TS: time recorded in the packet header, set on the sender side and extracted +from the packet at the receiver side + +Note that the timestamp in the packet's header is 32-bit, which gives +it more or less 2.5 minutes to roll over. Therefore timestamp +rollover is tracked and a segment increase is performed in order to keep an +eye on the overall actual time. For the needs of the formula definitions +it must be stated that TS is the true difference between the connection +start time and the time when the sending time has been declared when +the sender application is calling any of the `srt_send*` functions +(see [`srt_sendmsg2`](../API/API-functions.md#srt_sendmsg2) for details). + + +## SRT latency components + +To understand the latency components we need also other definitions: + +* **ETS** (Expected Time Stamp): The packet's expected arrival time, when it +"should" arrive according to its timestamp + +* **PTS** (Presentation Time Stamp): The packet's play time, when SRT gives the packet +to the `srt_recvmsg2` call (that is, it sets up the IN flag in epoll +and resumes the blocked function call, if it was in blocking mode). + +* **STS** (Sender Time Stamp): The time when the packet was +scheduled for sending at the sender side (if you don't use the +declared time, by default it's the monotonic time used when this +function is called). + +* **RTS** (Receiver Time Stamp): The same as STS, but calculated at the receiver side. The +only way to extract it is by using some initial statements. + +The "true latency" for a particular packet in SRT can be simply defined as: + +* `TD = PTS - STS` + +Note that this is a stable definition (independent of the packet), +but this value is not really controllable. So let's define the PTS +for a packet `x`: + +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +where `LATENCY` is the negotiated latency value (out of the +`SRTO_RCVLATENCY` on the agent and `SRTO_PEERLATENCY` on the peer) +and DRIFT will be described later (for simplification you can +state it's initially 0). + +These components undergo the following formula: + +* `ETS[x] = start_time + TS[x]` + +Note that it's not possible to simply define a "true" latency based on STS +because the sender and receiver are two different machines that can only +see one another through the network. Their clocks are separate, +and can even run at different or changing speeds, and the only +visible phenomena happen when packets arrive at the receiver machine. +However, the formula above does allow us to define the start time because +we state the following for the very first data packet: + +* `ETS[0] = ATS[0]` + +This means that from this formula we can define the start time: + +* `start_time = ATS[0] - TS[0]` + +Therefore we can state that if we have two identical clocks on +both machines with identical time bases and speeds, then: + +* `ATS[x] = program_delay[x] + network_delay[x] + STS[x]` + +Note that two machines communicating over a network do not typically have a +common clock base. Therefore, although this formula is correct, it involves +components that can neither be measured nor captured at the receiver side. + +This formula for ATS doesn't apply to the real latency, which is based strictly +on ETS. But you can apply this formula for the very first arriving packet, +because in this case they are equal: `ATS[0] = ETS[0]`. + +Therefore this formula is true for the very first packet: + +* `ETS[0] = prg_delay[0] + net_delay[0] + STS[0]` + +We know also that the TS set on the sender side is: + +* `TS[x] = STS[x] - snd_connect_time` + +Taking both formulas for ETS together: + +* `ETS[x] = start_time + TS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x]` + +we have then: + +* `start_time = prg_delay[0] + net_delay[0] + snd_connect_time` + +**IMPORTANT**: `start_time` is not the time of arrival of the first packet, +but that time taken backwards by using the delay already recorded in TS. As TS should +represent the delay towards `snd_connect_time`, `start_time` should be simply the same +as `snd_connect_time`, just on the receiver side, and so shifted by the +first packet's delays of `prg_delay` and `net_delay`. + +So, as we have the start time defined, the above formulas: + +* `ETS[x] = start_time + TS[x]` +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +now define the packet delivery time as: + +* `PTS[x] = start_time + TS[x] + LATENCY + DRIFT` + +and after replacing the start time we have: + +* `PTS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x] + LATENCY + DRIFT` + +and from the TS formula we get STS, so we replace it: + +* `PTS[x] = prg_delay[0] + net_delay[0] + STS[x] + LATENCY + DRIFT` + +We can now get the true network latency in SRT by moving STS to the other side: + +* `PTS[x] - STS[x] = prg_delay[0] + net_delay[0] + LATENCY + DRIFT` + + +## The DRIFT + +The DRIFT is a measure of the variance over time of the base time. +To simplify the calculations above, DRIFT is considered to be 0, +which is the initial state. In time, however, it changes based on the +value of the Arrival Time Deviation: + +* `ATD[x] = ATS[x] - ETS[x]` + +The drift is then calculated as: + +* `DRIFT[x] = average(ATD[x-N] ... ATD[x])` + +The value of the drift is tracked over an appropriate number of samples. If +it exceeds a threshold value, the drift value is applied to modify the +base time. However, as you can see from the formula for ATD, the drift is +simply taken from the actual time when the packet arrived, and the time +when it would have arrived if the `prg_delay` and `net_delay` values were +exactly the same as for the very first packet. ATD then represents the +changes in these values. There can be two main factors that could result +in having this value as non-zero: + +1. A phenomenon has been observed in several types of networks where +the very first packet arrives quickly, but as subsequent data packets +come in regularly, the network delay slightly increases and then remains fixed +for a long time at this increased value. This phenomenon can be +mitigated by having a reliable value for RTT. Once the increase is observed +a special factor could be applied to decrease the positive value +of the drift. This isn't currently implemented. This phenomenon also +isn't observed in every network, especially those covering longer distances. + +2. The clock speed on both machines (sender and receiver) isn't exactly the same, +which means that if you decipher the ETS basing on the TS, over time it may result +in values that even precede the STS (suggesting a negative network delay) or that +have an enormous delay (with ATS exceeding PTS). This is actually the main reason +for tracking the drift. From 52fe11c24adad57c0f462018f342738ac5dd5269 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 14:41:34 +0100 Subject: [PATCH 07/85] [core] Fixed the PacketFilter configuration not counting the AEAD AUTH tag (#2880). * Fixed build break due to const use of UniquePtr. --- srtcore/core.cpp | 10 +++++++++- srtcore/core.h | 1 + srtcore/fec.cpp | 3 +++ srtcore/packetfilter.cpp | 8 +++++++- srtcore/utilities.h | 2 +- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index dcb264993..b15f91287 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5649,6 +5649,14 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd return true; } +int srt::CUDT::getAuthTagSize() const +{ + if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + return HAICRYPT_AUTHTAG_MAX; + + return 0; +} + bool srt::CUDT::prepareBuffers(CUDTException* eout) { if (m_pSndBuffer) @@ -5660,7 +5668,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) try { // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. - const int authtag = (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) ? HAICRYPT_AUTHTAG_MAX : 0; + const int authtag = getAuthTagSize(); SRT_ASSERT(m_iMaxSRTPayloadSize != 0); diff --git a/srtcore/core.h b/srtcore/core.h index 9d5fa4c3a..08e03230c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -517,6 +517,7 @@ class CUDT /// Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool prepareBuffers(CUDTException* eout); + int getAuthTagSize() const; SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 9830a3bc8..fd762b88b 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -598,6 +598,9 @@ void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, g.flag_clip = g.flag_clip ^ kflg; g.timestamp_clip = g.timestamp_clip ^ timestamp_hw; + HLOGC(pflog.Debug, log << "FEC CLIP: data pkt.size=" << payload_size + << " to a clip buffer size=" << payloadSize()); + // Payload goes "as is". for (size_t i = 0; i < payload_size; ++i) { diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 37785f43a..9610f04e3 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -314,9 +314,15 @@ bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::strin init.socket_id = parent->socketID(); init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); - init.payload_size = parent->OPT_PayloadSize(); + + // XXX This is a formula for a full "SRT payload" part that undergoes transmission, + // might be nice to have this formula as something more general. + init.payload_size = parent->OPT_PayloadSize() + parent->getAuthTagSize(); init.rcvbuf_size = parent->m_config.iRcvBufSize; + HLOGC(pflog.Debug, log << "PFILTER: @" << init.socket_id << " payload size=" + << init.payload_size << " rcvbuf size=" << init.rcvbuf_size); + // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); if (!m_filter) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 258a2fdc8..3d9cf1c09 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () { return 0!= get(); } + operator bool () const { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From e0d4227ad3061d4cdc1dd8554648bc466c760780 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 16 Feb 2024 16:23:38 +0100 Subject: [PATCH 08/85] [core] Refax. Removed reference fields from CPacket pinned to the SRT header (#2594). --- srtcore/buffer_snd.cpp | 16 +-- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 223 ++++++++++++++++++----------------- srtcore/core.h | 2 +- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +-- test/test_crypto.cpp | 7 +- test/test_fec_rebuilding.cpp | 6 +- 13 files changed, 159 insertions(+), 150 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 3aa81042e..ef1bc498c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -317,7 +317,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -343,7 +343,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -358,7 +358,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: picked up packet to send: size=" << readlen << " #" << w_packet.getMsgSeq() - << " %" << w_packet.m_iSeqNo + << " %" << w_packet.seqno() << " !" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); break; @@ -449,7 +449,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // This is rexmit request, so the packet should have the sequence number // already set when it was once sent uniquely. - SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -493,8 +493,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // to simply take the sequence number from the block. But this is a new // feature and should be only used after refax for the sender buffer to // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. - w_drop.seqno[DropRange::BEGIN] = w_packet.m_iSeqNo; - w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + w_drop.seqno[DropRange::BEGIN] = w_packet.seqno(); + w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); // Note the rules: here `p` is pointing to the first block AFTER the // message to be dropped, so the end sequence should be one behind @@ -515,7 +515,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -523,7 +523,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 0b448d681..16ccc8c1b 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -683,8 +683,8 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif LOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << dsrc.str() << " " << packet.Info()); #endif diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b15f91287..905c10457 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1982,7 +1982,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2513,7 +2513,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2540,7 +2540,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3572,7 +3572,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(0); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3587,7 +3587,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3659,7 +3659,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3897,17 +3897,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? 0 : m_ConnRes.m_iID); bool status = true; @@ -3918,7 +3918,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3938,7 +3938,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (m_RejectReason == SRT_REJ_UNKNOWN) m_RejectReason = SRT_REJ_ROGUE; - sendRendezvousRejection(serv_addr, (request)); + sendRendezvousRejection(serv_addr, (reqpkt)); status = false; } } @@ -3955,8 +3955,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3968,7 +3968,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3978,16 +3978,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request, m_SourceAddr); + m_pSndQueue->sendto(serv_addr, reqpkt, m_SourceAddr); return status; } @@ -5877,15 +5877,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5899,10 +5899,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show() << " sourceIP=" << m_SourceAddr.str()); } @@ -5913,7 +5913,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::string& w_why) @@ -6159,7 +6159,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -7882,7 +7882,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7897,7 +7897,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7918,7 +7918,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7948,7 +7948,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); m_tsLastWarningTime = steady_clock::now(); @@ -7957,14 +7957,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7973,21 +7973,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -8074,7 +8074,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8278,7 +8278,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8976,20 +8976,20 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. enterCS(m_ConnectionLock); - bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (initdata)); + bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (initdata)); leaveCS(m_ConnectionLock); if (create_ok) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response, m_SourceAddr); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt, m_SourceAddr); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -9121,7 +9121,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9296,37 +9296,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9336,7 +9343,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; @@ -9388,7 +9395,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9493,7 +9500,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9505,14 +9512,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9521,7 +9528,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9632,14 +9639,14 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9649,7 +9656,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9668,7 +9675,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9768,7 +9775,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9782,7 +9789,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9790,7 +9797,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9799,7 +9806,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = current_sequence_number; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packUniqueData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9811,16 +9818,16 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Override extraction sequence with scheduling sequence. ScopedLock ackguard(m_RecvAckLock); - m_iSndCurrSeqNo = w_packet.m_iSeqNo; - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << current_sequence_number << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9832,15 +9839,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packUniqueData: Applying EXTRACTION sequence " << current_sequence_number - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = current_sequence_number; + w_packet.set_seqno(current_sequence_number); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9860,7 +9867,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -10029,7 +10036,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -10054,7 +10061,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -10067,7 +10074,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -10089,7 +10096,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -10100,7 +10107,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -10219,7 +10226,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -10233,12 +10240,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -10246,9 +10253,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -10296,7 +10303,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10340,7 +10347,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10366,7 +10373,7 @@ int srt::CUDT::processData(CUnit* in_unit) // supply the missing packet(s), and the loss will no longer be visible for the code that follows. if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing { - const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10382,13 +10389,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10454,7 +10461,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10739,7 +10746,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10759,7 +10766,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10974,7 +10981,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: - // [[using assert(packet.m_iID == 0)]] + // [[using assert(packet.id() == 0)]] HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); @@ -11062,7 +11069,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -11130,7 +11137,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -11178,8 +11185,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); } @@ -11290,7 +11297,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp, use_source_addr); } else @@ -11301,7 +11308,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); diff --git a/srtcore/core.h b/srtcore/core.h index 08e03230c..74b097efd 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -409,7 +409,7 @@ class CUDT static void setPacketTS(CPacket& p, const time_point& start_time, const time_point& ts) { - p.m_iTimeStamp = makeTS(ts, start_time); + p.set_timestamp(makeTS(ts, start_time)); } /// @brief Set the timestamp field of the packet using the provided value (no check) diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index fbb56a42c..b41ed6b9b 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -601,7 +597,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index 027d5f0b3..9b757118f 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -352,12 +352,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 9610f04e3..dc7e5b422 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index cf0016901..345151b4e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -900,7 +900,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : 0; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -1385,7 +1385,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 3d9cf1c09..9b9288cac 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1280,6 +1280,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index 132440368..dbe4b7179 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -244,7 +244,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probeArrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -266,7 +266,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probe1Arrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -276,7 +276,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -292,7 +292,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 0c67fa91c..554c6aae5 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,22 +54,25 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = msgno; - packet.m_iMsgNo |= PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = msgno; + pktMsgFlags |= PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index ce68dd32e..f4fa7f614 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -85,9 +85,9 @@ namespace srt const int inorder = 1; const int kflg = m_crypt.getSndCryptoFlags(); - pkt.m_iSeqNo = seqno; - pkt.m_iMsgNo = msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg);; - pkt.m_iTimeStamp = 356; + pkt.set_seqno(seqno); + pkt.set_msgflags(msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg)); + pkt.set_timestamp(356); std::iota(pkt.data(), pkt.data() + pld_size, '0'); pkt.setLength(pld_size); @@ -103,7 +103,6 @@ namespace srt // Modify the payload and expect auth to fail. pkt_enc->data()[10] = '5'; EXPECT_EQ(m_crypt.decrypt(*pkt_enc.get()), ENCS_FAILED); - } } // namespace srt diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 602ff6d13..581d406fd 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -809,7 +809,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -886,7 +886,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -904,7 +904,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); From ea8ea9f2c21d513e149b87bf28db9375ab146151 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:04:36 +0100 Subject: [PATCH 09/85] [build] Moved code coverage testing from travis to github actions (#2874). --- .github/workflows/cxx11-ubuntu.yaml | 9 +++++++-- .travis.yml | 7 +------ CMakeLists.txt | 6 +----- codecov.yml | 4 ++++ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 500ff1beb..40724d54b 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -16,8 +16,13 @@ jobs: - name: configure run: | mkdir _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON -DENABLE_CODE_COVERAGE=ON - name: build run: cd _build && cmake --build ./ - name: test - run: cd _build && ctest --extra-verbose + run: | + cd _build && ctest --extra-verbose + - name: codecov + run: | + source ./scripts/collect-gcov.sh + bash <(curl -s https://codecov.io/bash) diff --git a/.travis.yml b/.travis.yml index 72df01bf1..fafa5e2b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,8 @@ matrix: - os: linux env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' + - BUILD_OPTS='-DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - RUN_SONARCUBE=1 - - RUN_CODECOV=1 - env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_LOGGING=OFF -DUSE_ENCLIB=mbedtls -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' @@ -100,10 +99,6 @@ script: test $SUCCESS == 0; fi after_success: - - if (( "$RUN_CODECOV" )); then - source ./scripts/collect-gcov.sh; - bash <(curl -s https://codecov.io/bash); - fi - if (( "$RUN_SONARCUBE" )); then sonar-scanner -D"sonar.cfamily.gcov.reportPath=."; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b0e3cff..a429cf495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1522,15 +1522,11 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) ) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() - gtest_add_tests( - TEST_LIST tests_srt - TARGET test-srt - ) set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) + gtest_discover_tests(test-srt) endif() enable_testing() - endif() diff --git a/codecov.yml b/codecov.yml index f91e5c1fe..2716c7f60 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,3 +6,7 @@ coverage: threshold: null patch: false changes: false +ignore: + - "testing" + - "apps" + - "example" \ No newline at end of file From 99fa92160bb2164025b6003c35b3e77416e6e9a8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 20 Feb 2024 17:21:02 +0100 Subject: [PATCH 10/85] [core] Added getter for the SRTO_MAXREXMITBW socket option. --- srtcore/core.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 905c10457..3835a0f04 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -559,6 +559,15 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int32_t); break; +#ifdef ENABLE_MAXREXMITBW + case SRTO_MAXREXMITBW: + if (size_t(optlen) < sizeof(m_config.llMaxRexmitBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + *(int64_t*)optval = m_config.llMaxRexmitBW; + optlen = sizeof(int64_t); + break; +#endif + case SRTO_STATE: *(int32_t *)optval = uglobal().getStatus(m_SocketID); optlen = sizeof(int32_t); From bed548897962a3822ca08b5fdc76fc41bd5c6474 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 20 Feb 2024 17:21:29 +0100 Subject: [PATCH 11/85] [tests] Added unittest for SRTO_MAXREXMITBW socket option. --- test/test_socket_options.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 78388bf30..051a17330 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -189,6 +189,9 @@ const OptionTestEntry g_test_matrix_options[] = //SRTO_LINGER { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, //SRTO_MAXBW +#ifdef ENABLE_MAXREXMITBW + { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), -1ll, INT64_MAX, - 1ll, 200000ll, {-2ll}}, +#endif { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, //SRTO_MININPUTBW { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, @@ -234,7 +237,7 @@ template void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) { ValueType opt_val; - int opt_len = 0; + int opt_len = entry.opt_len; EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); From 07e22cac775c7f4e1abe17b8c2a845bd1f0616ed Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 21 Feb 2024 09:37:52 +0100 Subject: [PATCH 12/85] [tests] Added yielding for tests that spawn a thread for connect (#2883). --- test/test_fec_rebuilding.cpp | 29 ++++++++++++++++++++++++++++- test/test_socket_options.cpp | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 581d406fd..185b54e98 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -305,9 +305,12 @@ TEST(TestFEC, Connection) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); - SRTSOCKET la[] = { l }; + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + // Given 2s timeout for accepting as it has occasionally happened with Travis // that 1s might not be enough. + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); @@ -362,6 +365,9 @@ TEST(TestFEC, ConnectionReorder) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -417,6 +423,9 @@ TEST(TestFEC, ConnectionFull1) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -472,6 +481,9 @@ TEST(TestFEC, ConnectionFull2) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -527,6 +539,9 @@ TEST(TestFEC, ConnectionMess) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -580,6 +595,9 @@ TEST(TestFEC, ConnectionForced) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -630,6 +648,9 @@ TEST(TestFEC, RejectionConflict) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -671,6 +692,9 @@ TEST(TestFEC, RejectionIncompleteEmpty) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -716,6 +740,9 @@ TEST(TestFEC, RejectionIncomplete) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 051a17330..9f0b2b532 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -70,6 +70,9 @@ class TestSocketOptions }; auto accept_res = async(launch::async, accept_async, m_listen_sock); + // Make sure the thread was kicked + this_thread::yield(); + const int connect_res = Connect(); EXPECT_EQ(connect_res, SRT_SUCCESS); From 6b75145f4918af7dbba27ea2521ed474aa209e97 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 21 Feb 2024 20:49:05 +0100 Subject: [PATCH 13/85] [tests] Prevent socket from being closed twice in EE test (#2886). --- test/test_enforced_encryption.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 6fd284c23..717b4549c 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -257,8 +257,16 @@ class TestEnforcedEncryption { // Code here will be called just after the test completes. // OK to throw exceptions from here if needed. - EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); - EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + + if (m_caller_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); + } + + if (m_listener_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + } } @@ -541,6 +549,7 @@ class TestEnforcedEncryption // Just give it some time and close the socket. std::this_thread::sleep_for(std::chrono::milliseconds(50)); ASSERT_NE(srt_close(m_listener_socket), SRT_ERROR); + m_listener_socket = SRT_INVALID_SOCK; // mark closed already accepting_thread.join(); } } From b2d48bdf432d24b2b341273ecb876d7b990ea5e4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 09:34:57 +0100 Subject: [PATCH 14/85] [core] Fixed POST options setting on a connected group. The list of options was unordered, hence the binary search failed to find those options. --- srtcore/core.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 3835a0f04..4493c397d 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -119,6 +119,7 @@ const int UDT::ERROR = srt::CUDT::ERROR; 2[15..0]: TsbPD delay [0..60000] msec */ +// IMPORTANT!!! This array must be ordered by value, because std::binary_search is performed on it! extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_SNDSYN, SRTO_RCVSYN, @@ -127,11 +128,14 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_RCVTIMEO, SRTO_MAXBW, SRTO_INPUTBW, - SRTO_MININPUTBW, SRTO_OHEADBW, SRTO_SNDDROPDELAY, SRTO_DRIFTTRACER, + SRTO_MININPUTBW, SRTO_LOSSMAXTTL +#ifdef ENABLE_MAXREXMITBW + ,SRTO_MAXREXMITBW +#endif }; const int32_t From 29fda2c1d618133b1daee3807e1ff28dcd235f75 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 13:50:06 +0100 Subject: [PATCH 15/85] [tests] Added SRTO_MAXBW and SRTO_MININPUTBW to unit tests (#2888). --- test/test_socket_options.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 9f0b2b532..271d84186 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -191,12 +191,12 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_LATENCY, "SRTO_LATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 200, {-1} }, //SRTO_LINGER { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, - //SRTO_MAXBW + { SRTO_MAXBW, "SRTO_MAXBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, #ifdef ENABLE_MAXREXMITBW - { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), -1ll, INT64_MAX, - 1ll, 200000ll, {-2ll}}, + { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, #endif { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, - //SRTO_MININPUTBW + { SRTO_MININPUTBW, "SRTO_MININPUTBW", RestrictionType::POST, sizeof(int64_t), int64_t(0), INT64_MAX, int64_t(0), int64_t(200000), {int64_t(-1)}}, { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, @@ -240,7 +240,7 @@ template void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) { ValueType opt_val; - int opt_len = entry.opt_len; + int opt_len = (int) entry.opt_len; EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); From 16cb043d2c0897b620023bc3f4694691ffe86f7e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Feb 2024 13:29:39 +0100 Subject: [PATCH 16/85] [core] Fixed SRT_SOCKOPT_NPOST when ENABLE_MAXREXMITBW is defined. A follow up fix for #2889. --- srtcore/core.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srtcore/core.h b/srtcore/core.h index 74b097efd..10746c8c9 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -120,7 +120,12 @@ enum AckDataItem }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); +#ifdef ENABLE_MAXREXMITBW +static const size_t SRT_SOCKOPT_NPOST = 13; +#else static const size_t SRT_SOCKOPT_NPOST = 12; +#endif + extern const SRT_SOCKOPT srt_post_opt_list []; enum GroupDataItem From 04acbbad93a40a9c258b9c404adce1f96b1dacdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Feb 2024 17:12:57 +0100 Subject: [PATCH 17/85] [core] Fixed the problem of getting option values from groups --- srtcore/api.cpp | 2 +- srtcore/group.cpp | 86 +++++++++++++++++----------- test/test_bonding.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 32 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 554bed9fd..cb9e5e65d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1394,7 +1394,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i for (size_t i = 0; i < g.m_config.size(); ++i) { HLOGC(aclog.Debug, log << "groupConnect: OPTION @" << sid << " #" << g.m_config[i].so); - error_reason = "setting group-derived option: #" + Sprint(g.m_config[i].so); + error_reason = "group-derived option: #" + Sprint(g.m_config[i].so); ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], (int)g.m_config[i].value.size()); } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b9b0a0d1c..1539245a0 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -717,6 +717,16 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) return true; } +struct FOptionValue +{ + SRT_SOCKOPT expected; + FOptionValue(SRT_SOCKOPT v): expected(v) {} + bool operator()(const CUDTGroup::ConfigItem& i) const + { + return i.so == expected; + } +}; + void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) { // Options handled in group @@ -732,46 +742,60 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) w_optlen = sizeof(bool); return; - default:; // pass on - } + case SRTO_SNDTIMEO: + *(int*)pw_optval = m_iSndTimeOut; + w_optlen = sizeof(int); + return; - // XXX Suspicous: may require locking of GlobControlLock - // to prevent from deleting a socket in the meantime. - // Deleting a socket requires removing from the group first, - // so after GroupLock this will be either already NULL or - // a valid socket that will only be closed after time in - // the GC, so this is likely safe like all other API functions. - CUDTSocket* ps = 0; + case SRTO_RCVTIMEO: + *(int*)pw_optval = m_iRcvTimeOut; + w_optlen = sizeof(int); + return; - { - // In sockets. All sockets should have all options - // set the same and should represent the group state - // well enough. If there are no sockets, just use default. + case SRTO_GROUPMINSTABLETIMEO: + *(uint32_t*)pw_optval = m_uOPT_MinStabilityTimeout_us / 1000; + w_optlen = sizeof(uint32_t); + return; - // Group lock to protect the container itself. - // Once a socket is extracted, we state it cannot be - // closed without the group send/recv function or closing - // being involved. - ScopedLock lg(m_GroupLock); - if (m_Group.empty()) - { - if (!getOptDefault(optname, (pw_optval), (w_optlen))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Write-only options for security reasons or + // options that refer to a socket state, that + // makes no sense for a group. + case SRTO_PASSPHRASE: + case SRTO_KMSTATE: + case SRTO_PBKEYLEN: + case SRTO_KMPREANNOUNCE: + case SRTO_KMREFRESHRATE: + case SRTO_BINDTODEVICE: + case SRTO_GROUPCONNECT: + case SRTO_STATE: + case SRTO_EVENT: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - return; - } + default:; // pass on + } + + // Check if the option is in the storage, which means that + // it was modified on the group. - ps = m_Group.begin()->ps; + vector::const_iterator i = find_if(m_config.begin(), m_config.end(), + FOptionValue(optname)); - // Release the lock on the group, as it's not necessary, - // as well as it might cause a deadlock when combined - // with the others. + if (i == m_config.end()) + { + // Not found, see the defaults + if (!getOptDefault(optname, (pw_optval), (w_optlen))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + return; } - if (!ps) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Found, return the value from the storage. + // Check the size first. + if (w_optlen < int(i->value.size())) + throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); - return ps->core().getOpt(optname, (pw_optval), (w_optlen)); + w_optlen = i->value.size(); + memcpy((pw_optval), &i->value[0], i->value.size()); } SRT_SOCKSTATUS CUDTGroup::getStatus() diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index a03b7c5a0..241d26d99 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -330,3 +330,130 @@ TEST(Bonding, CloseGroupAndSocket) listen_promise.wait(); } +TEST(Bonding, Options) +{ + using namespace std; + using namespace srt; + + TestInit srtinit; + + // Create a group + const SRTSOCKET grp = srt_create_group(SRT_GTYPE_BROADCAST); + + // rendezvous shall not be allowed to be set on the group + // XXX actually it is possible, but no one tested it. POSTPONE. + //int yes = 1; + //EXPECT_EQ(srt_setsockflag(grp, SRTO_RENDEZVOUS, &yes, sizeof yes), SRT_ERROR); + +#ifdef SRT_ENABLE_ENCRYPTION + string pass = "longenoughpassword"; + // passphrase should be ok. + EXPECT_NE(srt_setsockflag(grp, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + + int lat = 500; + EXPECT_NE(srt_setsockflag(grp, SRTO_RCVLATENCY, &lat, sizeof lat), SRT_ERROR); + + mutex mx; + condition_variable latch; + atomic started {false}; + + thread accept_and_close { [&]() { + + unique_lock ux(mx); + + SRTSOCKET lsn = srt_create_socket(); +#ifdef SRT_ENABLE_ENCRYPTION + EXPECT_NE(srt_setsockflag(lsn, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + int allow = 1; + ASSERT_NE(srt_setsockflag(lsn, SRTO_GROUPCONNECT, &allow, sizeof allow), SRT_ERROR); + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + ASSERT_NE(srt_bind(lsn, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(lsn, 1), SRT_ERROR); + started = true; + + // First wait - until it's let go with accepting + latch.wait(ux); + + sockaddr_any revsa; + SRTSOCKET gs = srt_accept(lsn, revsa.get(), &revsa.len); + ASSERT_NE(gs, SRT_ERROR); + + // Connected, wait to close + latch.wait(ux); + + srt_close(gs); + srt_close(lsn); + }}; + + // Give the thread a chance to start + this_thread::yield(); + + while (!started) + { + // In case of a bad luck, just wait for the thread to + // acquire the mutex before you do + this_thread::sleep_for(chrono::milliseconds(10)); + } + + // Wait for the possibility to connect + { + // Make sure that the thread reached the wait() call. + unique_lock ux(mx); + latch.notify_all(); + } + + // Now the thread is accepting, so we call the connect. + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + SRTSOCKET member = srt_connect(grp, sa.get(), sa.size()); + + // We've released the mutex and signaled the CV, so accept should proceed now. + // Exit from srt_connect() means also exit from srt_accept(). + + EXPECT_NE(member, SRT_INVALID_SOCK); + + // conenct_res should be a socket + EXPECT_NE(member, 0); // XXX Change to SRT_SOCKID_CONNREQ + + // Now get the option value from the group + + int revlat = -1; + int optsize = sizeof revlat; + EXPECT_NE(srt_getsockflag(grp, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + revlat = -1; + optsize = sizeof revlat; + // Expect the same value set on the member socket + EXPECT_NE(srt_getsockflag(member, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + // Individual socket option modified on group + int ohead = 12; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(grp, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // Modifyting a post-option should be possible on a socket + ohead = 11; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(member, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // But getting the option value should be equal to the group setting + EXPECT_NE(srt_getsockflag(grp, SRTO_OHEADBW, &ohead, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof ohead); + EXPECT_EQ(ohead, 12); + + // We're done, the thread can close connection and exit + { + // Make sure that the thread reached the wait() call. + std::unique_lock ux(mx); + latch.notify_all(); + } + + accept_and_close.join(); + srt_close(grp); +} + From 12aad6e4396c03cb37a0c4275f069195ea47501d Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 29 Feb 2024 15:56:22 +0100 Subject: [PATCH 18/85] [tests] Improved tests to avoid all-breaking asserts and inter-test mess-up (#2892). --- CMakeLists.txt | 7 +- srtcore/api.cpp | 23 +- srtcore/logging.h | 2 +- test/test_env.h | 12 +- test/test_epoll.cpp | 22 +- test/test_file_transmission.cpp | 23 +- test/test_main.cpp | 14 +- test/test_many_connections.cpp | 14 +- test/test_reuseaddr.cpp | 595 ++++++++++++++++---------------- test/test_sync.cpp | 3 +- 10 files changed, 375 insertions(+), 340 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a429cf495..8951dc46c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,12 @@ if (NOT MICROSOFT) # that ENABLE_DEBUG is set as it should. if (ENABLE_DEBUG EQUAL 2) set (CMAKE_BUILD_TYPE "RelWithDebInfo") - add_definitions(-DNDEBUG) + if (ENABLE_ASSERT) + # Add _DEBUG macro if explicitly requested, to enable SRT_ASSERT(). + add_definitions(-D_DEBUG) + else() + add_definitions(-DNDEBUG) + endif() elseif (ENABLE_DEBUG) # 1, ON, YES, TRUE, Y, or any other non-zero number set (CMAKE_BUILD_TYPE "Debug") diff --git a/srtcore/api.cpp b/srtcore/api.cpp index cb9e5e65d..f9ac6a8d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2662,31 +2662,34 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { + CUDTSocket* ps = j->second; + CUDT& u = ps->core(); + // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); - if (!is_zero(j->second->core().m_tsLingerExpiration)) + if (!is_zero(u.m_tsLingerExpiration)) { // asynchronous close: - if ((!j->second->core().m_pSndBuffer) || (0 == j->second->core().m_pSndBuffer->getCurrBufSize()) || - (j->second->core().m_tsLingerExpiration <= steady_clock::now())) + if ((!u.m_pSndBuffer) || (0 == u.m_pSndBuffer->getCurrBufSize()) || + (u.m_tsLingerExpiration <= steady_clock::now())) { - HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << j->second->m_SocketID); - j->second->core().m_tsLingerExpiration = steady_clock::time_point(); - j->second->core().m_bClosing = true; - j->second->m_tsClosureTimeStamp = steady_clock::now(); + HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << ps->m_SocketID); + u.m_tsLingerExpiration = steady_clock::time_point(); + u.m_bClosing = true; + ps->m_tsClosureTimeStamp = steady_clock::now(); } } // timeout 1 second to destroy a socket AND it has been removed from // RcvUList const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; + const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp; if (closed_ago > seconds_from(1)) { - CRNode* rnode = j->second->core().m_pRNode; + CRNode* rnode = u.m_pRNode; if (!rnode || !rnode->m_bOnList) { HLOGC(smlog.Debug, - log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " + log << "checkBrokenSockets: @" << ps->m_SocketID << " closed " << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); // HLOGC(smlog.Debug, log << "will unref socket: " << j->first); diff --git a/srtcore/logging.h b/srtcore/logging.h index 608234eab..c17781c24 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -154,7 +154,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - bool enabled; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } diff --git a/test/test_env.h b/test/test_env.h index 68ec516b3..e20905351 100644 --- a/test/test_env.h +++ b/test/test_env.h @@ -63,18 +63,26 @@ class TestInit class UniqueSocket { int32_t sock; + std::string lab, f; + int l; public: - UniqueSocket(int32_t s): sock(s) + UniqueSocket(int32_t s, const char* label, const char* file, int line): sock(s) { if (s == -1) throw std::invalid_argument("Invalid socket"); + lab = label; + f = file; + l = line; } - UniqueSocket(): sock(-1) +#define MAKE_UNIQUE_SOCK(name, label, expr) srt::UniqueSocket name (expr, label, __FILE__, __LINE__) + + UniqueSocket(): sock(-1), l(0) { } + void close(); ~UniqueSocket(); operator int32_t() const diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 116978c08..a8a88aa7a 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -66,7 +66,7 @@ TEST(CEPoll, WaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -89,7 +89,7 @@ TEST(CEPoll, UWaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -112,7 +112,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -146,7 +146,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -174,7 +174,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -197,7 +197,7 @@ TEST(CEPoll, HandleEpollEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -258,7 +258,7 @@ TEST(CEPoll, NotifyConnectionBreak) srt::TestInit srtinit; // 1. Prepare client - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes SRT_ATR_UNUSED = 1; @@ -280,7 +280,7 @@ TEST(CEPoll, NotifyConnectionBreak) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa_client.sin_addr), 1); // 2. Prepare server - srt::UniqueSocket server_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(server_sock, "server_sock", srt_create_socket()); ASSERT_NE(server_sock, SRT_ERROR); ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect @@ -372,7 +372,7 @@ TEST(CEPoll, HandleEpollEvent2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -433,7 +433,7 @@ TEST(CEPoll, HandleEpollNoEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -483,7 +483,7 @@ TEST(CEPoll, ThreadedUpdate) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int no = 0; diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 97f9e684a..bfd668ac7 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -25,12 +25,14 @@ #include #include #include +#include //#pragma comment (lib, "ws2_32.lib") TEST(Transmission, FileUpload) { srt::TestInit srtinit; + srtinit.HandlePerTestOptions(); // Generate the source file // We need a file that will contain more data @@ -93,7 +95,7 @@ TEST(Transmission, FileUpload) // Start listener-receiver thread - bool thread_exit = false; + std::atomic thread_exit { false }; auto client = std::thread([&] { @@ -116,12 +118,17 @@ TEST(Transmission, FileUpload) for (;;) { int n = srt_recv(accepted_sock, buf.data(), 1456); - ASSERT_NE(n, SRT_ERROR); + EXPECT_NE(n, SRT_ERROR) << srt_getlasterror_str(); if (n == 0) { std::cerr << "Received 0 bytes, breaking.\n"; break; } + else if (n == -1) + { + std::cerr << "READ FAILED, breaking anyway\n"; + break; + } // Write to file any amount of data received copyfile.write(buf.data(), n); @@ -173,7 +180,7 @@ TEST(Transmission, FileUpload) std::cout << "Sockets closed, joining receiver thread\n"; client.join(); - std::ifstream tarfile("file.target"); + std::ifstream tarfile("file.target", std::ios::in | std::ios::binary); EXPECT_EQ(!!tarfile, true); tarfile.seekg(0, std::ios::end); @@ -182,8 +189,14 @@ TEST(Transmission, FileUpload) std::cout << "Comparing files\n"; // Compare files - tarfile.seekg(0, std::ios::end); - ifile.seekg(0, std::ios::beg); + + // Theoretically it should work if you just rewind to 0, but + // on Windows this somehow doesn't work. + tarfile.close(); + tarfile.open("file.target", std::ios::in | std::ios::binary); + + ifile.close(); + ifile.open("file.source", std::ios::in | std::ios::binary); for (size_t i = 0; i < tar_size; ++i) { diff --git a/test/test_main.cpp b/test/test_main.cpp index cc5acc487..cb3b7c5c5 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -113,6 +113,11 @@ void TestInit::HandlePerTestOptions() { srt_setloglevel(LOG_DEBUG); } + + if (TestEnv::me->OptionPresent("lognote")) + { + srt_setloglevel(LOG_NOTICE); + } } // Copied from ../apps/apputil.cpp, can't really link this file here. @@ -178,7 +183,14 @@ sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_f UniqueSocket::~UniqueSocket() { - srt_close(sock); + // Could be closed explicitly + if (sock != -1) + close(); +} + +void UniqueSocket::close() +{ + EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; } } diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 20deccf70..29cd6bdd2 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -135,6 +135,8 @@ TEST_F(TestConnection, Multiple) cerr << "Opening " << NSOCK << " connections\n"; + bool overall_test = true; + for (size_t i = 0; i < NSOCK; i++) { m_connections[i] = srt_create_socket(); @@ -145,13 +147,18 @@ TEST_F(TestConnection, Multiple) int conntimeo = 60; srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); + SRTSOCKET connres = SRT_INVALID_SOCK; + //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n"; //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl; - ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR); + connres = srt_connect(m_connections[i], psa, sizeof lsa); + EXPECT_NE(connres, SRT_INVALID_SOCK) << "conn #" << i << ": " << srt_getlasterror_str(); + if (connres == SRT_INVALID_SOCK) + overall_test = false; // Set now async sending so that sending isn't blocked int no = 0; - ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); + EXPECT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); } for (size_t j = 1; j <= 100; j++) @@ -170,6 +177,7 @@ TEST_F(TestConnection, Multiple) EXPECT_FALSE(m_accept_exit) << "AcceptLoop already broken for some reason!"; // Up to this moment the server sock should survive + cerr << "Closing server socket\n"; // Close server socket to break the accept loop EXPECT_EQ(srt_close(m_server_sock), 0); @@ -177,6 +185,8 @@ TEST_F(TestConnection, Multiple) cerr << "Synchronize with the accepting thread\n"; ex.wait(); cerr << "Synchronization done\n"; + + ASSERT_TRUE(overall_test); } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index fe9027311..fa92bc635 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifndef _WIN32 #include #endif @@ -30,7 +31,7 @@ struct AtReturnJoin // iphlp library to be attached to the executable, which is kinda // problematic. Temporarily block tests using this function on Windows. -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n"; return ""; @@ -50,7 +51,7 @@ struct IfAddr } }; -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; @@ -101,306 +102,364 @@ std::string GetLocalIP(int af = AF_UNSPEC) } #endif -int client_pollid = SRT_ERROR; -SRTSOCKET g_client_sock = SRT_ERROR; - -void clientSocket(std::string ip, int port, bool expect_success) +class ReuseAddr : public srt::Test { - int yes = 1; - int no = 0; - - int family = AF_INET; - std::string famname = "IPv4"; - if (ip.substr(0, 2) == "6.") - { - family = AF_INET6; - ip = ip.substr(2); - famname = "IPv6"; - } + int m_server_pollid = SRT_ERROR; - std::cout << "[T/C] Creating client socket\n"; +protected: - g_client_sock = srt_create_socket(); - ASSERT_NE(g_client_sock, SRT_ERROR); + std::string showEpollContents(const char* label, int* array, int length) + { + std::ostringstream out; + out << label << ":["; + if (length) + { + // Now is at least 1 + out << "@" << array[0]; - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockflag(g_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + for (int i = 1; i < length; ++i) + out << " @" << array[i]; + } + out << "]"; + return out.str(); + } - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + struct UniquePollid + { + int pollid = SRT_ERROR; + UniquePollid() + { + pollid = srt_epoll_create(); + } - int epoll_out = SRT_EPOLL_OUT; - srt_epoll_add_usock(client_pollid, g_client_sock, &epoll_out); + ~UniquePollid() + { + srt_epoll_release(pollid); + } - sockaddr_any sa = srt::CreateAddr(ip, port, family); + operator int() const + { + return pollid; + } + }; - std::cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << std::endl; + void clientSocket(SRTSOCKET client_sock, std::string ip, int port, bool expect_success) + { + using namespace std; - int connect_res = srt_connect(g_client_sock, sa.get(), sa.size()); + int yes = 1; + int no = 0; - if (connect_res == -1) - { - std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl; - } + int family = AF_INET; + string famname = "IPv4"; + if (ip.substr(0, 2) == "6.") + { + family = AF_INET6; + ip = ip.substr(2); + famname = "IPv6"; + } - if (expect_success) - { - EXPECT_NE(connect_res, -1); - if (connect_res == -1) - return; + cout << "[T/C] Setting up client socket\n"; + ASSERT_NE(client_sock, SRT_INVALID_SOCK); + ASSERT_EQ(srt_getsockstate(client_sock), SRTS_INIT); - // Socket readiness for connection is checked by polling on WRITE allowed sockets. + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - if (connect_res != -1) - { - int rlen = 2; - SRTSOCKET read[2]; + UniquePollid client_pollid; + ASSERT_NE(int(client_pollid), SRT_ERROR); - int wlen = 2; - SRTSOCKET write[2]; + int epoll_out = SRT_EPOLL_OUT; + srt_epoll_add_usock(client_pollid, client_sock, &epoll_out); - std::cout << "[T/C] Waiting for connection readiness...\n"; + sockaddr_any sa = srt::CreateAddr(ip, port, family); - EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR); + cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << endl; + int connect_res = srt_connect(client_sock, sa.get(), sa.size()); - EXPECT_EQ(rlen, 0); // get exactly one write event without reads - EXPECT_EQ(wlen, 1); // get exactly one write event without reads - EXPECT_EQ(write[0], g_client_sock); // for our client socket + if (connect_res == -1) + { + cout << "srt_connect: " << srt_getlasterror_str() << endl; + } - char buffer[1316] = {1, 2, 3, 4}; - EXPECT_NE(srt_sendmsg(g_client_sock, buffer, sizeof buffer, - -1, // infinit ttl - true // in order must be set to true - ), - SRT_ERROR); + if (expect_success) + { + EXPECT_NE(connect_res, -1); + if (connect_res == -1) + return; + + // Socket readiness for connection is checked by polling on WRITE allowed sockets. + + if (connect_res != -1) + { + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + cout << "[T/C] Waiting for connection readiness...\n"; + + EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); + + EXPECT_EQ(rlen, 0) << showEpollContents("[T/C] R", read, rlen); // get exactly one write event without reads + EXPECT_EQ(wlen, 1) << showEpollContents("[T/C] W", write, wlen); // get exactly one write event without reads + EXPECT_EQ(write[0], client_sock); // for our client socket + + char buffer[1316] = {1, 2, 3, 4}; + EXPECT_NE(srt_sendmsg(client_sock, buffer, sizeof buffer, + -1, // infinit ttl + true // in order must be set to true + ), + SRT_ERROR); + } + else + { + cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + } } else { - std::cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + EXPECT_EQ(connect_res, -1); } - } - else - { - EXPECT_EQ(connect_res, -1); + + cout << "[T/C] Client exit\n"; } - std::cout << "[T/C] Client exit\n"; -} + SRTSOCKET prepareServerSocket() + { + SRTSOCKET bindsock = srt_create_socket(); + EXPECT_NE(bindsock, SRT_ERROR); -int server_pollid = SRT_ERROR; + int yes = 1; + int no = 0; -SRTSOCKET prepareSocket() -{ - SRTSOCKET bindsock = srt_create_socket(); - EXPECT_NE(bindsock, SRT_ERROR); + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - int yes = 1; - int no = 0; + int epoll_in = SRT_EPOLL_IN; - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to m_server_pollid\n"; + srt_epoll_add_usock(m_server_pollid, bindsock, &epoll_in); - int epoll_in = SRT_EPOLL_IN; + return bindsock; + } - std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; - srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); + bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) + { + sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); - return bindsock; -} + std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; -bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); + std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; - std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; + int bind_res = srt_bind(bindsock, sa.get(), sa.size()); - std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; + std::cout << "[T/S] ... result " << bind_res << " (expected to " + << (expect_success ? "succeed" : "fail") << ")\n"; - int bind_res = srt_bind(bindsock, sa.get(), sa.size()); + if (!expect_success) + { + std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; + EXPECT_EQ(bind_res, SRT_ERROR); + return false; + } - std::cout << "[T/S] ... result " << bind_res << " (expected to " - << (expect_success ? "succeed" : "fail") << ")\n"; + EXPECT_NE(bind_res, SRT_ERROR); + return true; + } - if (!expect_success) + bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; - EXPECT_EQ(bind_res, SRT_ERROR); - return false; - } + if (!bindSocket(bindsock, ip, port, expect_success)) + return false; - EXPECT_NE(bind_res, SRT_ERROR); - return true; -} + EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); -bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - if (!bindSocket(bindsock, ip, port, expect_success)) - return false; + return true; + } - EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); + SRTSOCKET createListener(std::string ip, int port, bool expect_success) + { + std::cout << "[T/S] serverSocket: creating listener socket\n"; - return true; -} + SRTSOCKET bindsock = prepareServerSocket(); -SRTSOCKET createListener(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating listener socket\n"; + if (!bindListener(bindsock, ip, port, expect_success)) + return SRT_INVALID_SOCK; - SRTSOCKET bindsock = prepareSocket(); + return bindsock; + } - if (!bindListener(bindsock, ip, port, expect_success)) - return SRT_INVALID_SOCK; + SRTSOCKET createBinder(std::string ip, int port, bool expect_success) + { + std::cout << "[T/S] serverSocket: creating binder socket\n"; - return bindsock; -} + SRTSOCKET bindsock = prepareServerSocket(); -SRTSOCKET createBinder(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating binder socket\n"; + if (!bindSocket(bindsock, ip, port, expect_success)) + { + srt_close(bindsock); + return SRT_INVALID_SOCK; + } - SRTSOCKET bindsock = prepareSocket(); + return bindsock; + } - if (!bindSocket(bindsock, ip, port, expect_success)) + void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - srt_close(bindsock); - return SRT_INVALID_SOCK; - } + MAKE_UNIQUE_SOCK(client_sock, "[T/C]connect", srt_create_socket()); - return bindsock; -} + auto run = [this, &client_sock, ip, port, expect_success]() { clientSocket(client_sock, ip, port, expect_success); }; -void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ + auto launched = std::async(std::launch::async, run); - auto run = [ip, port, expect_success]() { clientSocket(ip, port, expect_success); }; + AtReturnJoin atreturn_join {launched}; - auto launched = std::async(std::launch::async, run); + { // wait for connection from client + int rlen = 2; + SRTSOCKET read[2]; - AtReturnJoin atreturn_join {launched}; + int wlen = 2; + SRTSOCKET write[2]; - { // wait for connection from client - int rlen = 2; - SRTSOCKET read[2]; + std::cout << "[T/S] Wait 10s on E" << m_server_pollid << " for acceptance on @" << bindsock << " ...\n"; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_NE(srt_epoll_wait(m_server_pollid, + read, &rlen, + write, &wlen, + 10000, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - std::cout << "[T/S] Wait 10s for acceptance on @" << bindsock << " ...\n"; - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - 10000, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + ASSERT_EQ(read[0], bindsock); // read event is for bind socket + } + { + sockaddr_any scl; + MAKE_UNIQUE_SOCK(accepted_sock, "[T/S]accept", srt_accept(bindsock, scl.get(), &scl.len)); - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], bindsock); // read event is for bind socket - } + if (accepted_sock == -1) + { + std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; + } + EXPECT_NE(accepted_sock.ref(), SRT_INVALID_SOCK); - sockaddr_any scl; + sockaddr_any showacp = (sockaddr*)&scl; + std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; - SRTSOCKET accepted_sock = srt_accept(bindsock, scl.get(), &scl.len); - if (accepted_sock == -1) - { - std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; - } - ASSERT_NE(accepted_sock, SRT_INVALID_SOCK); + int epoll_in = SRT_EPOLL_IN; + srt_epoll_add_usock(m_server_pollid, accepted_sock, &epoll_in); // wait for input - sockaddr_any showacp = (sockaddr*)&scl; - std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; + char buffer[1316]; + { // wait for 1316 packet from client + int rlen = 2; + SRTSOCKET read[2]; - int epoll_in = SRT_EPOLL_IN; - srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input + int wlen = 2; + SRTSOCKET write[2]; - char buffer[1316]; - { // wait for 1316 packet from client - int rlen = 2; - SRTSOCKET read[2]; + std::cout << "[T/S] Wait for data reception...\n"; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_NE(srt_epoll_wait(m_server_pollid, + read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - std::cout << "[T/S] Wait for data reception...\n"; - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + EXPECT_EQ(read[0], accepted_sock.ref()); // read event is for bind socket + } + char pattern[4] = {1, 2, 3, 4}; - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], accepted_sock); // read event is for bind socket - } + EXPECT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), + 1316); - char pattern[4] = {1, 2, 3, 4}; + EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); - ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), - 1316); + // XXX There is a possibility that a broken socket can be closed automatically, + // just the srt_close() call would simply return error in case of nonexistent + // socket. Therefore close them both at once; this problem needs to be fixed + // separately. + // + // The test only intends to send one portion of data from the client, so once + // received, the client has nothing more to do and should exit. + std::cout << "[T/S] closing client socket\n"; + client_sock.close(); + std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << "...\n"; + } + // client_sock closed through UniqueSocket. + // cannot close client_sock after srt_sendmsg because of issue in api.c:2346 - EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); + std::cout << "[T/S] joining client async \n"; + launched.get(); + } - std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << " LSN:@" << bindsock << " CLR:@" << g_client_sock << " ...\n"; - ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); - ASSERT_NE(srt_close(g_client_sock), SRT_ERROR); // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + static void shutdownListener(SRTSOCKET bindsock) + { + // Silently ignore. Usually it should have been checked earlier, + // and an invalid sock might be expected in particular tests. + if (bindsock == SRT_INVALID_SOCK) + return; - std::cout << "[T/S] joining client async...\n"; - launched.get(); -} + int yes = 1; + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect + EXPECT_NE(srt_close(bindsock), SRT_ERROR); -void shutdownListener(SRTSOCKET bindsock) -{ - // Silently ignore. Usually it should have been checked earlier, - // and an invalid sock might be expected in particular tests. - if (bindsock == SRT_INVALID_SOCK) - return; + std::chrono::milliseconds check_period (100); + int credit = 400; // 10 seconds + auto then = std::chrono::steady_clock::now(); - int yes = 1; - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect - EXPECT_NE(srt_close(bindsock), SRT_ERROR); + std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; + while (srt_getsockstate(bindsock) != SRTS_NONEXIST) + { + std::this_thread::sleep_for(check_period); + --credit; + if (!credit) + break; + } + auto now = std::chrono::steady_clock::now(); + auto dur = std::chrono::duration_cast(now - then); - std::chrono::milliseconds check_period (100); - int credit = 400; // 10 seconds - auto then = std::chrono::steady_clock::now(); + // Keep as single string because this tends to be mixed from 2 threads. + std::ostringstream sout; + sout << "[T/S] @" << bindsock << " dissolved after " + << (dur.count() / 1000.0) << "s" << std::endl; + std::cout << sout.str() << std::flush; - std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; - while (srt_getsockstate(bindsock) != SRTS_NONEXIST) - { - std::this_thread::sleep_for(check_period); - --credit; - if (!credit) - break; + EXPECT_NE(credit, 0); } - auto now = std::chrono::steady_clock::now(); - auto dur = std::chrono::duration_cast(now - then); - - // Keep as single string because this tends to be mixed from 2 threads. - std::ostringstream sout; - sout << "[T/S] @" << bindsock << " dissolved after " - << (dur.count() / 1000.0) << "s" << std::endl; - std::cout << sout.str() << std::flush; - EXPECT_NE(credit, 0); -} +private: -TEST(ReuseAddr, SameAddr1) -{ - srt::TestInit srtinit; + void setup() + { + m_server_pollid = srt_epoll_create(); + ASSERT_NE(m_server_pollid, SRT_ERROR); + } - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); + void teardown() + { + (void)srt_epoll_release(m_server_pollid); + m_server_pollid = SRT_ERROR; + } +}; - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); +TEST_F(ReuseAddr, SameAddr1) +{ SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener("127.0.0.1", 5000, true); @@ -413,24 +472,14 @@ TEST(ReuseAddr, SameAddr1) s1.join(); s2.join(); - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddr2) +TEST_F(ReuseAddr, SameAddr2) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder(localip, 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -445,21 +494,11 @@ TEST(ReuseAddr, SameAddr2) testAccept(bindsock_3, localip, 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddrV6) +TEST_F(ReuseAddr, SameAddrV6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); SRTSOCKET bindsock_1 = createBinder("::1", 5000, true); SRTSOCKET bindsock_2 = createListener("::1", 5000, true); @@ -475,26 +514,15 @@ TEST(ReuseAddr, SameAddrV6) testAccept(bindsock_3, "::1", 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, DiffAddr) +TEST_F(ReuseAddr, DiffAddr) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -505,14 +533,10 @@ TEST(ReuseAddr, DiffAddr) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard) +TEST_F(ReuseAddr, Wildcard) { - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -524,14 +548,6 @@ TEST(ReuseAddr, Wildcard) std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // Binding a certain address when wildcard is already bound should fail. @@ -541,15 +557,11 @@ TEST(ReuseAddr, Wildcard) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard6) +TEST_F(ReuseAddr, Wildcard6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -567,17 +579,10 @@ TEST(ReuseAddr, Wildcard6) // performed there. std::string localip_v4 = GetLocalIP(AF_INET); - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - // This must be obligatory set before binding a socket to "::" int strict_ipv6 = 1; - SRTSOCKET bindsock_1 = prepareSocket(); + SRTSOCKET bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -601,7 +606,7 @@ TEST(ReuseAddr, Wildcard6) strict_ipv6 = 0; - bindsock_1 = prepareSocket(); + bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -620,33 +625,23 @@ TEST(ReuseAddr, Wildcard6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersion6) +TEST_F(ReuseAddr, ProtocolVersion6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int yes = 1; @@ -659,32 +654,23 @@ TEST(ReuseAddr, ProtocolVersion6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersionFaux6) +TEST_F(ReuseAddr, ProtocolVersionFaux6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; + #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int no = 0; @@ -696,7 +682,4 @@ TEST(ReuseAddr, ProtocolVersionFaux6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 844705ea6..e0454a581 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -587,7 +587,8 @@ TEST(SyncEvent, WaitForNotifyAll) /*****************************************************************************/ void* dummythread(void* param) { - *(bool*)(param) = true; + auto& thread_finished = *(srt::sync::atomic*)param; + thread_finished = true; return nullptr; } From 710c2022c94d9f05765fc1b900ad540424ba2ec0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 6 Mar 2024 15:31:08 +0100 Subject: [PATCH 19/85] [core] Withdrawn changes from #2858 as they break ABI compat (#2899). --- srtcore/api.cpp | 5 ---- srtcore/logging.cpp | 63 --------------------------------------------- srtcore/logging.h | 50 ++++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 83 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index f9ac6a8d1..a3734a603 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,18 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; - srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); - srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); - srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4675,6 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); - srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4683,6 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); - srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d309b1b8a..ee9270b03 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,69 +23,6 @@ using namespace std; namespace srt_logging { -// Note: subscribe() and unsubscribe() functions are being called -// in the global constructor and destructor only, as the -// Logger objects (and inside them also their LogDispatcher) -// are being created. It's not predicted that LogDispatcher -// object are going to be created any other way than as -// global objects. Therefore the construction and destruction -// of them happens always in the main thread. - -void LogConfig::subscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - return; // Do not register twice - - loggers.push_back(lg); -} - -void LogConfig::unsubscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - { - loggers.erase(p); - } -} - -// This function doesn't have any protection on itself, -// however the API functions from which it is called, call -// it already under a mutex protection. -void LogConfig::updateLoggersState() -{ - for (vector::iterator p = loggers.begin(); - p != loggers.end(); ++p) - { - (*p)->Update(); - } -} - -void LogDispatcher::Update() -{ - bool enabled_in_fa = src_config->enabled_fa[fa]; - enabled = enabled_in_fa && level <= src_config->max_level; -} - - -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - src_config->log_stream->flush(); - } - src_config->unlock(); -} - #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index c17781c24..993f8ad2c 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,6 @@ written by #include #include #include -#include #include #include #ifdef _WIN32 @@ -116,7 +115,6 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,10 +137,6 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } - - void subscribe(LogDispatcher*); - void unsubscribe(LogDispatcher*); - void updateLoggersState(); }; // The LogDispatcher class represents the object that is responsible for @@ -154,7 +148,6 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -165,7 +158,6 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), - enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -193,18 +185,13 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } - config.subscribe(this); - Update(); } ~LogDispatcher() { - src_config->unsubscribe(this); } - void Update(); - - bool CheckEnabled() { return enabled; } + bool CheckEnabled(); void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,6 +415,22 @@ class Logger } }; +inline bool LogDispatcher::CheckEnabled() +{ + // Don't use enabler caching. Check enabled state every time. + + // These assume to be atomically read, so the lock is not needed + // (note that writing to this field is still mutex-protected). + // It's also no problem if the level was changed at the moment + // when the enabler check is tested here. Worst case, the log + // will be printed just a moment after it was turned off. + const LogConfig* config = src_config; // to enforce using const operator[] + int configured_enabled_fa = config->enabled_fa[fa]; + int configured_maxlevel = config->max_level; + + return configured_enabled_fa && level <= configured_maxlevel; +} + #if HAVE_CXX11 @@ -478,7 +481,24 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); } -#endif // INC_SRT_LOGGING_H +} +#endif // INC_SRT_LOGGING_H From 7f490242e4dad2c4247e3cf173703e05bc9b6606 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:32:06 +0100 Subject: [PATCH 20/85] [build] Add a github action for big endian testing (#2898). --- .github/workflows/s390x-focal.yaml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/s390x-focal.yaml diff --git a/.github/workflows/s390x-focal.yaml b/.github/workflows/s390x-focal.yaml new file mode 100644 index 000000000..f1b6c7508 --- /dev/null +++ b/.github/workflows/s390x-focal.yaml @@ -0,0 +1,41 @@ +name: QEMU to run s390x-focal + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + Tests: + runs-on: ubuntu-latest + steps: + - name: Setup multiarch/qemu-user-static + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + - name: ubuntu-core:s390x-focal + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "uname -a && + lscpu | grep Endian + " + - name: Checkout + uses: actions/checkout@v3 + - name: configure + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "apt-get -y update && + export DEBIAN_FRONTEND=noninteractive && + export TZ=Etc/UTC && + apt-get -y install tzdata && + uname -a && + lscpu | grep Endian && + apt-get -y install cmake g++ libssl-dev git && + mkdir _build && cd _build && + cmake ../ -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON && + cmake --build ./ && + ./test-srt -disable-ipv6" From 618ddfed45a53d99e4a0ce8528b2ecfe2860b5bc Mon Sep 17 00:00:00 2001 From: Tommy Wu <7903172+tommyvct@users.noreply.github.com> Date: Thu, 7 Mar 2024 03:45:03 -0600 Subject: [PATCH 21/85] [build] Fix: link bcrypt on Windows when mbedtls >=v3.5.0 (#2860). Only link to bcrypt if mbedtls is not found by find_package(mbedtls). --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8951dc46c..c5994d3b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,11 +365,11 @@ if (ENABLE_ENCRYPTION) set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) endif() + if (WIN32) + set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) + endif() if ("${SSL_LIBRARIES}" STREQUAL "") set (SSL_LIBRARIES mbedtls mbedcrypto) - if (WIN32) - set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) - endif() endif() message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") From 9b8aaa6a1bea8adb2f55bf7ea898857be71c229f Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:51:11 +0100 Subject: [PATCH 22/85] [core] Fix some warnings when compiling with MinGW (#2868). Removed duplicate declaration of SRT_API. Moved around variables to avoid set but not used warnings. Removed SRT_API attribute of SysStrError. --- srtcore/channel.cpp | 15 +++++++------- srtcore/platform_sys.h | 14 +++++++++++++ srtcore/srt.h | 46 +++++++++++++++++------------------------- srtcore/srt_compat.c | 1 - srtcore/srt_compat.h | 45 +---------------------------------------- 5 files changed, 41 insertions(+), 80 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 16ccc8c1b..557be8fd7 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -51,7 +51,6 @@ modified by *****************************************************************************/ #include "platform_sys.h" - #include #include // Logging #include @@ -189,6 +188,7 @@ void srt::CChannel::createSocket(int family) m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; #endif + #else // ENABLE_SOCK_CLOEXEC m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); #endif // ENABLE_SOCK_CLOEXEC @@ -197,17 +197,18 @@ void srt::CChannel::createSocket(int family) throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); #if ENABLE_SOCK_CLOEXEC -#ifdef _WIN32 - // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) -#else + if (cloexec_flag) { +#ifdef _WIN32 + // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) +#else if (0 != set_cloexec(m_iSocket, 1)) { throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); } +#endif //_WIN32 } -#endif #endif // ENABLE_SOCK_CLOEXEC if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) @@ -795,8 +796,8 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka { if (NET_ERROR == WSA_IO_PENDING) { - res = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); - if (res == WAIT_FAILED) + DWORD res_wait = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); + if (res_wait == WAIT_FAILED) { LOGC(kslog.Warn, log << "CChannel::WSAWaitForMultipleEvents: failed with " << NET_ERROR); res = -1; diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index e2f0aa4d9..83763e5ea 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -21,6 +21,20 @@ // // SRT_IMPORT_TIME (mach time on Mac, portability gettimeofday on WIN32) // SRT_IMPORT_EVENT (includes kevent on Mac) +#ifdef _WIN32 + #ifndef __MINGW32__ + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + #endif +#endif #ifdef _WIN32 diff --git a/srtcore/srt.h b/srtcore/srt.h index 53b6fd274..3c60f3086 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -16,10 +16,28 @@ written by #ifndef INC_SRTC_H #define INC_SRTC_H +#ifndef SRT_API +#ifdef _WIN32 + #ifdef SRT_DYNAMIC + #ifdef SRT_EXPORTS + #define SRT_API __declspec(dllexport) + #else + #define SRT_API __declspec(dllimport) + #endif + #else // !SRT_DYNAMIC + #define SRT_API + #endif +#else + #define SRT_API __attribute__ ((visibility("default"))) +#endif +#endif + #include "version.h" #include "platform_sys.h" +#include "srt_compat.h" + #include #include @@ -33,34 +51,6 @@ written by //if compiling with MinGW, it only works on XP or above //use -D_WIN32_WINNT=0x0501 - -#ifdef _WIN32 - #ifndef __MINGW32__ - // Explicitly define 32-bit and 64-bit numbers - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int32 uint32_t; - #ifndef LEGACY_WIN32 - typedef unsigned __int64 uint64_t; - #else - // VC 6.0 does not support unsigned __int64: may cause potential problems. - typedef __int64 uint64_t; - #endif - #endif - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else // !SRT_DYNAMIC - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif - - // For feature tests if you need. // You can use these constants with SRTO_MINVERSION option. #define SRT_VERSION_FEAT_HSv5 0x010300 diff --git a/srtcore/srt_compat.c b/srtcore/srt_compat.c index fbf4859ae..bbdf7f795 100644 --- a/srtcore/srt_compat.c +++ b/srtcore/srt_compat.c @@ -17,7 +17,6 @@ written by // Prevents from misconfiguration through preprocessor. #include "platform_sys.h" - #include #include diff --git a/srtcore/srt_compat.h b/srtcore/srt_compat.h index 960c1b85a..d4fd2361e 100644 --- a/srtcore/srt_compat.h +++ b/srtcore/srt_compat.h @@ -20,55 +20,12 @@ written by #include #include -#ifndef SRT_API -#ifdef _WIN32 - #ifndef __MINGW32__ - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else - #define SRT_API - #endif - #else - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif -#endif - -#ifdef _WIN32 - // https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "Id" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "Iu" - #endif -#else - // http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "zd" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "zu" - #endif -#endif - - #ifdef __cplusplus extern "C" { #endif /* Ensures that we store the error in the buffer and return the bufer. */ -SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen); +const char * SysStrError(int errnum, char * buf, size_t buflen); #ifdef __cplusplus } // extern C From 2c3f6f97d650e9903a982e46bb29b8641a416dc4 Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:32:49 +0100 Subject: [PATCH 23/85] [core] Fix some indirect inclusion issues (#2906). * Removed unnecessary include of srt_compat.h from srt.h. * Removed include of srt_compat.h and directly added it where it belongs. * Added srt_compat.h include to srt-test-live. * Added cstddef include to packet.cpp. --- apps/srt-live-transmit.cpp | 2 +- apps/srt-tunnel.cpp | 1 + srtcore/common.cpp | 2 -- srtcore/logging.cpp | 2 +- srtcore/logging.h | 1 - srtcore/packet.cpp | 2 +- srtcore/srt.h | 2 -- testing/srt-test-live.cpp | 1 + 8 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index a4ed93a29..0a918e6b0 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -65,7 +65,7 @@ #include #include - +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 569a0c26e..90bd43eae 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -27,6 +27,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/srtcore/common.cpp b/srtcore/common.cpp index fe5217dfd..6d747ecaa 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -68,8 +68,6 @@ modified by #include "packet.h" #include "threadname.h" -#include // SysStrError - using namespace std; using namespace srt::sync; using namespace srt_logging; diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index ee9270b03..d0ba3fd4a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -14,7 +14,7 @@ written by *****************************************************************************/ - +#include "srt_compat.h" #include "logging.h" using namespace std; diff --git a/srtcore/logging.h b/srtcore/logging.h index 993f8ad2c..3abfe0326 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -33,7 +33,6 @@ written by #include "utilities.h" #include "threadname.h" #include "logging_api.h" -#include "srt_compat.h" #include "sync.h" #ifdef __GNUC__ diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index b41ed6b9b..668c4b5b3 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -159,7 +159,7 @@ modified by // the original sequence numbers in the field. #include "platform_sys.h" - +#include #include #include "packet.h" #include "handshake.h" diff --git a/srtcore/srt.h b/srtcore/srt.h index 3c60f3086..614a85aea 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -36,8 +36,6 @@ written by #include "platform_sys.h" -#include "srt_compat.h" - #include #include diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..17f4020dc 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -64,6 +64,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" From 03aa5e41125169733558854aa7a527f73c2adcc6 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 8 Mar 2024 14:50:16 +0100 Subject: [PATCH 24/85] [core] Formal errors fixed (#2907). --- apps/verbose.hpp | 2 +- srtcore/logging.h | 14 +++++----- srtcore/utilities.h | 3 ++- testing/srt-test-mpbond.cpp | 7 ++--- testing/srt-test-relay.cpp | 1 + testing/testactivemedia.cpp | 3 +++ testing/testactivemedia.hpp | 12 ++++----- testing/testmedia.hpp | 52 ++++++++++++++++++------------------- 8 files changed, 49 insertions(+), 45 deletions(-) diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 10591888b..879d54086 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -80,7 +80,7 @@ inline void Print(Log& ) {} template inline void Print(Log& out, Arg1&& arg1, Args&&... args) { - out << arg1; + out << std::forward(arg1); Print(out, args...); } diff --git a/srtcore/logging.h b/srtcore/logging.h index 3abfe0326..2ec5f46aa 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -203,16 +203,16 @@ struct SRT_API LogDispatcher template void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args); - template - void operator()(Arg1&& arg1, Args&&... args) + template + void operator()(Args&&... args) { - PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...); + PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", args...); } - template - void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args) + template + void printloc(const char* file, int line, const std::string& area, Args&&... args) { - PrintLogLine(file, line, area, arg1, args...); + PrintLogLine(file, line, area, args...); } #else template @@ -440,7 +440,7 @@ inline void PrintArgs(std::ostream&) {} template inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) { - serr << arg1; + serr << std::forward(arg1); PrintArgs(serr, args...); } diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 9b9288cac..8a1374eb7 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -35,6 +35,7 @@ written by #include #include #include +#include #if HAVE_CXX11 #include @@ -575,7 +576,7 @@ inline Stream& Print(Stream& in) { return in;} template inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) { - sout << arg1; + sout << std::forward(arg1); return Print(sout, args...); } diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index 03066363a..bf9739675 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -42,6 +42,10 @@ srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-mpbond"); +using namespace srt; +using namespace std; + + volatile bool mpbond_int_state = false; void OnINT_SetIntState(int) { @@ -49,9 +53,6 @@ void OnINT_SetIntState(int) mpbond_int_state = true; } -using namespace srt; - - int main( int argc, char** argv ) { // This is mainly required on Windows to initialize the network system, diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index dfabef62b..45d657128 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -45,6 +45,7 @@ written by #include "threadname.h" +using namespace std; bool Upload(UriParser& srt, UriParser& file); diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 7d062f49d..96344f0b2 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -1,6 +1,9 @@ #include "testactivemedia.hpp" +using namespace std; + + void SourceMedium::Runner() { srt::ThreadName::set("SourceRN"); diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index f4bc360ba..011dcbfe7 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -59,7 +59,7 @@ struct Medium std::ostringstream tns; tns << typeid(*this).name() << ":" << this; srt::ThreadName tn(tns.str()); - thr = thread( [this] { RunnerBase(); } ); + thr = std::thread( [this] { RunnerBase(); } ); } void quit() @@ -89,12 +89,12 @@ struct Medium if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); else - cerr << "Transmission Error: " << e.what() << endl; + std::cerr << "Transmission Error: " << e.what() << std::endl; } catch (...) { if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; else - cerr << "UNKNOWN EXCEPTION on medium\n"; + std::cerr << "UNKNOWN EXCEPTION on medium\n"; } } @@ -150,7 +150,7 @@ struct TargetMedium: Medium bool Schedule(const MediaPacket& data) { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); if (!running || ::transmit_int_state) { @@ -166,13 +166,13 @@ struct TargetMedium: Medium void Clear() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); buffer.clear(); } void Interrupt() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); running = false; ready.notify_one(); } diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..be72471d1 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -29,8 +29,6 @@ extern std::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; -using namespace std; - const srt_logging::LogFA SRT_LOGFA_APP = 10; extern srt_logging::Logger applog; @@ -57,7 +55,7 @@ class SrtCommon struct ConnectionBase { - string host; + std::string host; int port; int weight = 0; SRTSOCKET socket = SRT_INVALID_SOCK; @@ -65,7 +63,7 @@ class SrtCommon srt::sockaddr_any target; int token = -1; - ConnectionBase(string h, int p): host(h), port(p), source(AF_INET) {} + ConnectionBase(std::string h, int p): host(h), port(p), source(AF_INET) {} }; struct Connection: ConnectionBase @@ -76,7 +74,7 @@ class SrtCommon int error = SRT_SUCCESS; int reason = SRT_REJ_UNKNOWN; - Connection(string h, int p): ConnectionBase(h, p) {} + Connection(std::string h, int p): ConnectionBase(h, p) {} Connection(Connection&& old): ConnectionBase(old) { #if ENABLE_BONDING @@ -101,14 +99,14 @@ class SrtCommon int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_direction bool m_tsbpdmode = true; int m_outgoing_port = 0; - string m_mode; - string m_adapter; - map m_options; // All other options, as provided in the URI - vector m_group_nodes; - string m_group_type; - string m_group_config; + std::string m_mode; + std::string m_adapter; + std::map m_options; // All other options, as provided in the URI + std::vector m_group_nodes; + std::string m_group_type; + std::string m_group_config; #if ENABLE_BONDING - vector m_group_data; + std::vector m_group_data; #ifdef SRT_OLD_APP_READER int32_t m_group_seqno = -1; @@ -117,7 +115,7 @@ class SrtCommon int32_t sequence; bytevector packet; }; - map m_group_positions; + std::map m_group_positions; SRTSOCKET m_group_active; // The link from which the last packet was delivered #endif #endif @@ -131,8 +129,8 @@ class SrtCommon void UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size); public: - void InitParameters(string host, string path, map par); - void PrepareListener(string host, int port, int backlog); + void InitParameters(std::string host, std::string path, std::map par); + void PrepareListener(std::string host, int port, int backlog); void StealFrom(SrtCommon& src); void AcceptNewClient(); @@ -150,22 +148,22 @@ class SrtCommon protected: - void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); - void Init(string host, int port, string path, map par, SRT_EPOLL_OPT dir); + void Error(std::string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); + void Init(std::string host, int port, std::string path, std::map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); virtual int ConfigurePost(SRTSOCKET sock); virtual int ConfigurePre(SRTSOCKET sock); - void OpenClient(string host, int port); + void OpenClient(std::string host, int port); #if ENABLE_BONDING void OpenGroupClient(); #endif void PrepareClient(); void SetupAdapter(const std::string& host, int port); - void ConnectClient(string host, int port); - void SetupRendezvous(string adapter, string host, int port); + void ConnectClient(std::string host, int port); + void SetupRendezvous(std::string adapter, std::string host, int port); - void OpenServer(string host, int port, int backlog = 1) + void OpenServer(std::string host, int port, int backlog = 1) { PrepareListener(host, port, backlog); if (transmit_accept_hook_fn) @@ -175,7 +173,7 @@ class SrtCommon AcceptNewClient(); } - void OpenRendezvous(string adapter, string host, int port) + void OpenRendezvous(std::string adapter, std::string host, int port) { PrepareClient(); SetupRendezvous(adapter, host, port); @@ -284,11 +282,11 @@ class SrtModel: public SrtCommon public: bool is_caller = false; bool is_rend = false; - string m_host; + std::string m_host; int m_port = 0; - SrtModel(string host, int port, map par); + SrtModel(std::string host, int port, std::map par); void Establish(std::string& w_name); void Close() @@ -320,7 +318,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon bool eof = true; public: - UdpSource(string host, int port, const map& attr); + UdpSource(std::string host, int port, const std::map& attr); MediaPacket Read(size_t chunk) override; @@ -331,7 +329,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon class UdpTarget: public virtual Target, public virtual UdpCommon { public: - UdpTarget(string host, int port, const map& attr); + UdpTarget(std::string host, int port, const std::map& attr); void Write(const MediaPacket& data) override; bool IsOpen() override { return m_sock != -1; } @@ -341,7 +339,7 @@ class UdpTarget: public virtual Target, public virtual UdpCommon class UdpRelay: public Relay, public UdpSource, public UdpTarget { public: - UdpRelay(string host, int port, const map& attr): + UdpRelay(std::string host, int port, const std::map& attr): UdpSource(host, port, attr), UdpTarget(host, port, attr) { From 0a046d4c2072a5e1f8f02b8883934824a550f1d8 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 11:44:02 +0100 Subject: [PATCH 25/85] [build] Migrate SonarCloud to GitHub Action (#2910). --- .github/workflows/cxx11-ubuntu.yaml | 15 ++++++++++++--- .travis.yml | 21 +-------------------- sonar-project.properties | 6 +++--- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 40724d54b..d52afd31b 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -5,20 +5,23 @@ on: branches: [ master ] pull_request: branches: [ master ] - + types: [opened, synchronize, reopened] jobs: build: name: ubuntu runs-on: ubuntu-20.04 - + env: + BUILD_WRAPPER_OUT_DIR: sonar-output # Directory where build-wrapper output will be placed steps: - uses: actions/checkout@v3 + - name: Install sonar-scanner and build-wrapper + uses: sonarsource/sonarcloud-github-c-cpp@v2 - name: configure run: | mkdir _build && cd _build cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON -DENABLE_CODE_COVERAGE=ON - name: build - run: cd _build && cmake --build ./ + run: cd _build && build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build . - name: test run: | cd _build && ctest --extra-verbose @@ -26,3 +29,9 @@ jobs: run: | source ./scripts/collect-gcov.sh bash <(curl -s https://codecov.io/bash) + - name: Run SonarCloud Scan for C and C++ + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # Consult https://docs.sonarcloud.io/advanced-setup/ci-based-analysis/sonarscanner-cli/ for more information and options. + run: sonar-scanner --define sonar.cfamily.build-wrapper-output=_build/"${{ env.BUILD_WRAPPER_OUT_DIR }}" diff --git a/.travis.yml b/.travis.yml index fafa5e2b8..94d5dac3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,6 @@ addons: - build-essential - libmbedtls-dev - gdb - sonarcloud: - organization: "haivision" - token: - secure: "wJZC0kyyjuf4SZyonZ6p/5Ga9asEqSnKWF9NpRbu6S6ceERO7vbebuSJF5qX3A6ivPuw0TTk5WASOdnvIyfA28FU/D0MWRdH8K7T3w77wdE9EgAEYTUXzdrbzJY18+9pxjljHwWXWALPSGf3MClg4irWrdk1e6uHK+68R39+ZvBGBFpWeeZy/+at9+xwhtAGKBlSHe8zc+3wPxuYdvviLVJ25qbpNmnzkUR0X89G+UBl90raCPSN32EHFdImHZ5DxfEQQJgZFRjzQUY4EW/iYwaMel7jufAq0ClgV4psKujl9Lz8cPqx3WgqRfJyiIthOMTsac7G4zAw8LK2CI0VsssBp0JalLXaumi6vG7o6c3rIwKckzSKccq3pHa7h45praIVVn9s3nq+Q/JGA11FMkKQxdQtmwgFsLhbi6ZxabgsUi5KtWoWY2z6MgpJuROuAjNxZi9XJzUoJs7zSTUtRRW7V8Q2lRiOnknYh25N6TCA5bpyy1EZmRdJErm071YNI9P01gbFz5137FWJFiJzro9TGF0KoHSGiCIdUt3WlMzwr/i/wFLxFBQOZQ2rjTXvhs4hxONxMZV3gzxA1NdLaf9i5Mh6jxVMV+ujaRSV7JmPGzxqiAlpT9cJUhTCYuar9diLLeDrpe7RawEZR8V1xVDQ7yT8ruDNQ78VbSn/sC0=" homebrew: update: false packages: @@ -26,7 +22,6 @@ matrix: env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - - RUN_SONARCUBE=1 - env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_LOGGING=OFF -DUSE_ENCLIB=mbedtls -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' @@ -80,17 +75,7 @@ script: export PKG_CONFIG_PATH=$(brew --prefix openssl)"/lib/pkgconfig"; cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="ON"; fi - - echo "TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG" - - echo "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" - - if [[ "$TRAVIS_REPO_SLUG" != "Haivision/srt" || "$TRAVIS_PULL_REQUEST" -gt 0 ]]; then - export RUN_SONARCUBE=0; - fi - - echo "RUN_SONARCUBE=$RUN_SONARCUBE" - - if (( "$RUN_SONARCUBE" )); then - build-wrapper-linux-x86-64 --out-dir bw-output make; - else - make -j$(nproc); - fi + - make -j$(nproc); - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ulimit -c unlimited; ./test-srt -disable-ipv6; @@ -98,7 +83,3 @@ script: if [ -f core ]; then gdb -batch ./test-srt -c core -ex bt -ex "info thread" -ex quit; else echo "NO CORE - NO CRY!"; fi; test $SUCCESS == 0; fi -after_success: - - if (( "$RUN_SONARCUBE" )); then - sonar-scanner -D"sonar.cfamily.gcov.reportPath=."; - fi diff --git a/sonar-project.properties b/sonar-project.properties index 22b16d549..d11323df8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -19,7 +19,6 @@ sonar.organization=haivision # ===================================================== sonar.links.homepage=https://github.com/Haivision/srt -sonar.links.ci=https://travis-ci.org/Haivision/srt sonar.links.scm=https://github.com/Haivision/srt sonar.links.issue=https://github.com/Haivision/srt/issues @@ -29,8 +28,9 @@ sonar.links.issue=https://github.com/Haivision/srt/issues # ===================================================== # SQ standard properties -sonar.sources=. +sonar.sources=srtcore/,apps/,common/,examples/,haicrypt/,scripts/,testing/ +sonar.tests=test/ # Properties specific to the C/C++ analyzer: -sonar.cfamily.build-wrapper-output=bw-output +sonar.cfamily.build-wrapper-output=_build/sonar-output sonar.cfamily.gcov.reportsPath=. From 62ebfb90fe02283d2adace3f1ac4e7ddf83b3584 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 11:46:18 +0100 Subject: [PATCH 26/85] [core] A minor fix (reference to const). --- srtcore/api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a3734a603..d3e8af887 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2926,7 +2926,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++i) { - CMultiplexer& m = i->second; + CMultiplexer const& m = i->second; // First, we need to find a multiplexer with the same port. if (m.m_iPort != port) From 94bfa8682c654d22162f0f569ee8d9f6846625aa Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 12 Mar 2024 12:08:19 +0100 Subject: [PATCH 27/85] [build] Don't run a SonarScan on forks. --- .github/workflows/cxx11-ubuntu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index d52afd31b..aa74e1fce 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -30,6 +30,7 @@ jobs: source ./scripts/collect-gcov.sh bash <(curl -s https://codecov.io/bash) - name: Run SonarCloud Scan for C and C++ + if: ${{ !github.event.pull_request.head.repo.fork }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From aa69c3db0daa221b0a9b4e86fee2efd80c6e6f2b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 13 Mar 2024 10:36:33 +0100 Subject: [PATCH 28/85] [tests] Fixed tests that were weirdly failing (#2908). --- test/test_main.cpp | 21 ++++++++++++++++++++- test/test_reuseaddr.cpp | 28 ++++++++++++---------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/test/test_main.cpp b/test/test_main.cpp index cb3b7c5c5..e2243a306 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -190,7 +190,26 @@ UniqueSocket::~UniqueSocket() void UniqueSocket::close() { - EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; + int close_result = srt_close(sock); + int close_error = srt_getlasterror(nullptr); + + // XXX SRT_EINVSOCK is reported when the socket + // has been already wiped out, which may happen to a broken socket. + // This isn't exactly intended, although trying to close a nonexistent + // socket is not a problem, as long as it happens before the id value rollover + // (that is, when it's closed immediately after getting broken). + // This solution is still slick though and should be fixed. + // + // Restore this, when fixed + // EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; + if (close_result == SRT_ERROR) + { + EXPECT_NE(close_error, SRT_EINVSOCK) << lab << " CREATED: "<< f << ":" << l; + } + else + { + EXPECT_EQ(close_result, 0) << lab << " CREATED: "<< f << ":" << l; + } } } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index fa92bc635..d00628e63 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -104,8 +104,6 @@ static std::string GetLocalIP(int af = AF_UNSPEC) class ReuseAddr : public srt::Test { - int m_server_pollid = SRT_ERROR; - protected: std::string showEpollContents(const char* label, int* array, int length) @@ -243,11 +241,6 @@ class ReuseAddr : public srt::Test EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - int epoll_in = SRT_EPOLL_IN; - - std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to m_server_pollid\n"; - srt_epoll_add_usock(m_server_pollid, bindsock, &epoll_in); - return bindsock; } @@ -314,7 +307,7 @@ class ReuseAddr : public srt::Test void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) { - MAKE_UNIQUE_SOCK(client_sock, "[T/C]connect", srt_create_socket()); + MAKE_UNIQUE_SOCK(client_sock, "[T/S]connect", srt_create_socket()); auto run = [this, &client_sock, ip, port, expect_success]() { clientSocket(client_sock, ip, port, expect_success); }; @@ -322,6 +315,11 @@ class ReuseAddr : public srt::Test AtReturnJoin atreturn_join {launched}; + int server_pollid = srt_epoll_create(); + int epoll_in = SRT_EPOLL_IN; + std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; + srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); + { // wait for connection from client int rlen = 2; SRTSOCKET read[2]; @@ -329,9 +327,9 @@ class ReuseAddr : public srt::Test int wlen = 2; SRTSOCKET write[2]; - std::cout << "[T/S] Wait 10s on E" << m_server_pollid << " for acceptance on @" << bindsock << " ...\n"; + std::cout << "[T/S] Wait 10s on E" << server_pollid << " for acceptance on @" << bindsock << " ...\n"; - EXPECT_NE(srt_epoll_wait(m_server_pollid, + EXPECT_NE(srt_epoll_wait(server_pollid, read, &rlen, write, &wlen, 10000, // -1 is set for debuging purpose. @@ -358,7 +356,7 @@ class ReuseAddr : public srt::Test std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; int epoll_in = SRT_EPOLL_IN; - srt_epoll_add_usock(m_server_pollid, accepted_sock, &epoll_in); // wait for input + srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input char buffer[1316]; { // wait for 1316 packet from client @@ -370,7 +368,7 @@ class ReuseAddr : public srt::Test std::cout << "[T/S] Wait for data reception...\n"; - EXPECT_NE(srt_epoll_wait(m_server_pollid, + EXPECT_NE(srt_epoll_wait(server_pollid, read, &rlen, write, &wlen, -1, // -1 is set for debuging purpose. @@ -401,6 +399,8 @@ class ReuseAddr : public srt::Test client_sock.close(); std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << "...\n"; } + srt_epoll_release(server_pollid); + // client_sock closed through UniqueSocket. // cannot close client_sock after srt_sendmsg because of issue in api.c:2346 @@ -447,14 +447,10 @@ class ReuseAddr : public srt::Test void setup() { - m_server_pollid = srt_epoll_create(); - ASSERT_NE(m_server_pollid, SRT_ERROR); } void teardown() { - (void)srt_epoll_release(m_server_pollid); - m_server_pollid = SRT_ERROR; } }; From 84b5bb8c2bfd331ac55aba70fe09e5d3cbb00dee Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:50:53 +0100 Subject: [PATCH 29/85] [build] Add a GitHub CI Action to check API/ABI compatibility (#2902). --- .github/workflows/abi.yml | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/abi.yml diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml new file mode 100644 index 000000000..2c05cc06b --- /dev/null +++ b/.github/workflows/abi.yml @@ -0,0 +1,61 @@ +name: ABI checks + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + SRT_BASE: v1.5.0 + +jobs: + build: + name: ABI checks + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + with: + path: pull_request + - name: configure + run: | + cd pull_request + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build + run: | + sudo apt install -y abi-dumper + cd pull_request/_build && cmake --build ./ + make install DESTDIR=./installdir + SRT_TAG_VERSION=$(cat version.h |grep SRT_VERSION_MINOR |head -n1 |awk {'print $3'}) + abi-dumper libsrt.so -o libsrt-pr.dump -public-headers installdir/usr/local/include/srt/ -lver 0 + SRT_BASE="v1.$SRT_TAG_VERSION.0" + echo "SRT_BASE=$SRT_BASE" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + path: tag + ref: ${{ env.SRT_BASE }} + - name: configure_tag + run: | + echo $SRT_TAG_VERSION + cd tag + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build_tag + run: | + cd tag + cd _build && cmake --build ./ + make install DESTDIR=./installdir + abi-dumper libsrt.so -o libsrt-tag.dump -public-headers installdir/usr/local/include/srt/ -lver 1 + - name: abi-check + run: | + git clone https://github.com/lvc/abi-compliance-checker.git + cd abi-compliance-checker && sudo make install && cd ../ + abi-compliance-checker -l libsrt -old tag/_build/libsrt-tag.dump -new pull_request/_build/libsrt-pr.dump + RES=$? + if (( $RES != 0 )) + then + echo "ABI/API Compatibility check failed with value $?" + exit $RES + fi From c6afa19d53d2740e6bca6e3eba591ee92373046b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= Date: Thu, 4 Apr 2024 16:03:20 +0200 Subject: [PATCH 30/85] [core] Fix HaiCrypt_Clone(): set up RX crypto ctx properly (#2905). --- haicrypt/hcrypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c index 2568654b1..dc3f06801 100644 --- a/haicrypt/hcrypt.c +++ b/haicrypt/hcrypt.c @@ -320,6 +320,7 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT; memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt)); cryptoClone->ctx_pair[0].salt_len = 0; + cryptoClone->ctx = &cryptoClone->ctx_pair[0]; } *phhc = (void *)cryptoClone; From a15cf4e9f97228f45a59719972fe47bba9e77b50 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Apr 2024 12:16:47 +0200 Subject: [PATCH 31/85] [core] Drop noenc packets if RcvKmState is "secured". --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4493c397d..2e6551423 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10190,7 +10190,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& #endif } } - else if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + else if (m_pCryptoControl && m_pCryptoControl->m_RcvKmState == SRT_KM_S_SECURED) { // Unencrypted packets are not allowed. const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING); From 83077aa90c9d9b875577c410a75a7ee91f655c3d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Apr 2024 14:34:36 +0200 Subject: [PATCH 32/85] [docs] Update the SRT_KM_S_SECURED description. Updated pktRcvUndecrypted description. --- docs/API/API-socket-options.md | 2 +- docs/API/statistics.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 84e361f6d..a06f8556d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -58,7 +58,7 @@ Exchange for the initial key is done in the handshake. - `SRT_KM_S_SECURED` (`2`): KM exchange was successful and the data will be sent encrypted and will be decrypted by the receiver. This state is only possible on -both sides in both directions simultaneously. +both sides in both directions simultaneously. Any unencrypted packet will be dropped by the receiver. - `SRT_KM_S_NOSECRET` (`3`): If this state is in the sending direction (`SRTO_SNDKMSTATE`), then it means that the sending party has set a passphrase, but the peer did not. diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 60bba59ee..bc34ecca8 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -245,6 +245,8 @@ Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTD #### pktRcvUndecryptTotal The total number of packets that failed to be decrypted at the receiver side. Available for receiver. +The statistic also counts unencrypted packets that were expected to be uncrypted on a secured connection (see [SRTO_KM_S_SECURED](API-socket-options.md#srt_km_state)) +and hence dropped as not encrypted (undecrypted). #### pktSndFilterExtraTotal @@ -822,4 +824,4 @@ The ratio of unrecovered by the socket group packets `Dropped Packets Ratio` can ``` Dropped Packets Ratio = pktRcvDropTotal / pktSentUniqueTotal; in case both sender and receiver statistics is available Dropped Packets Ratio = pktRcvDropTotal / (pktRecvUniqueTotal + pktRcvDropTotal); in case receiver only statistics is available -``` \ No newline at end of file +``` From c156dab75b83c12bb8dbbda77dab569db22b3b78 Mon Sep 17 00:00:00 2001 From: yomnes0 Date: Fri, 5 Apr 2024 15:23:39 +0200 Subject: [PATCH 33/85] [build] Switch default enclib from openssl to openssl-evp --- CMakeLists.txt | 4 ++-- docs/build/build-options.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5994d3b7..1c30e49ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,12 +237,12 @@ if (NOT USE_ENCLIB) message("NOTE: USE_GNUTLS is deprecated. Use -DUSE_ENCLIB=gnutls instead.") set (USE_ENCLIB gnutls) else() - set (USE_ENCLIB openssl) + set (USE_ENCLIB openssl-evp) endif() endif() set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") -set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "gnutls" "mbedtls" "botan") +set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "openssl-evp" "gnutls" "mbedtls" "botan") # Make sure DLLs and executabes go to the same path regardles of subdirectory set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 88fcb85bb..529bd5cd7 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -597,8 +597,8 @@ remember that: Encryption library to be used. Possible options for ``: -* openssl (default) -* openssl-evp (OpenSSL EVP API, since 1.5.1) +* openssl-evp (default) +* openssl * gnutls (with nettle) * mbedtls * botan From 3b84386e16f9f12cab50b2b8f2d1970ef6c9078a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 11 Apr 2024 11:37:38 +0200 Subject: [PATCH 34/85] [core] Use common functions for byte order conversion. --- srtcore/channel.cpp | 24 +++--------------------- srtcore/packet.cpp | 29 ++++++++++------------------- srtcore/packet.h | 6 ++++-- srtcore/utilities.h | 9 ++++++--- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 557be8fd7..035904a39 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -748,7 +748,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif // convert control information into network order - packet.toNL(); + packet.toNetworkByteOrder(); #ifndef _WIN32 msghdr mh; @@ -818,7 +818,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka res = (0 == res) ? size : -1; #endif - packet.toHL(); + packet.toHostByteOrder(); return res; } @@ -1067,25 +1067,7 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet } w_packet.setLength(recv_size - CPacket::HDR_SIZE); - - // convert back into local host order - // XXX use NtoHLA(). - // for (int i = 0; i < 4; ++ i) - // w_packet.m_nHeader[i] = ntohl(w_packet.m_nHeader[i]); - { - uint32_t* p = w_packet.m_nHeader; - for (size_t i = 0; i < SRT_PH_E_SIZE; ++i) - { - *p = ntohl(*p); - ++p; - } - } - - if (w_packet.isControl()) - { - for (size_t j = 0, n = w_packet.getLength() / sizeof(uint32_t); j < n; ++j) - *((uint32_t*)w_packet.m_pcData + j) = ntohl(*((uint32_t*)w_packet.m_pcData + j)); - } + w_packet.toHostByteOrder(); return RST_OK; diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 668c4b5b3..180623039 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -432,38 +432,29 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } } -void CPacket::toNL() +void CPacket::toNetworkByteOrder() { - // XXX USE HtoNLA! + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t i = 0, n = getLength() / 4; i < n; ++i) - *((uint32_t*)m_pcData + i) = htonl(*((uint32_t*)m_pcData + i)); + HtoNLA((uint32_t*) m_pcData, (const uint32_t*) m_pcData, getLength() / 4); } - // convert packet header into network order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int j = 0; j < 4; ++j) - { - *p = htonl(*p); - ++p; - } + HtoNLA(p, p, 4); } -void CPacket::toHL() +void CPacket::toHostByteOrder() { - // convert back into local host order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int k = 0; k < 4; ++k) - { - *p = ntohl(*p); - ++p; - } + NtoHLA(p, p, 4); + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t l = 0, n = getLength() / 4; l < n; ++l) - *((uint32_t*)m_pcData + l) = ntohl(*((uint32_t*)m_pcData + l)); + NtoHLA((uint32_t*)m_pcData, (const uint32_t*)m_pcData, getLength() / 4); } } diff --git a/srtcore/packet.h b/srtcore/packet.h index 9b757118f..5094247b5 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -331,8 +331,10 @@ class CPacket }; public: - void toNL(); - void toHL(); + /// @brief Convert the packet inline to a network byte order (Little-endian). + void toNetworkByteOrder(); + /// @brief Convert the packet inline to a host byte order. + void toHostByteOrder(); protected: // DynamicStruct is the same as array of given type and size, just it diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 8a1374eb7..1786cf0ae 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -237,17 +237,20 @@ written by #endif -// Hardware <--> Network (big endian) convention +/// Hardware --> Network (big-endian) byte order conversion +/// @param size source length in four octets inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = htonl(src[i]); + dst[i] = htobe32(src[i]); } +/// Network (big-endian) --> Hardware byte order conversion +/// @param size source length in four octets inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = ntohl(src[i]); + dst[i] = be32toh(src[i]); } // Hardware <--> Intel (little endian) convention From df9b1f68e9aba46cc9b52d233628e4251b1b3ce3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 16 Apr 2024 15:27:35 +0200 Subject: [PATCH 35/85] [core] Revert PR #2834. Overlapped send with 100 ms timeout on Windows added a data race. --- srtcore/channel.cpp | 47 ++++++++++----------------------------------- srtcore/channel.h | 3 --- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 035904a39..2fd1b3805 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -142,14 +142,6 @@ srt::CChannel::CChannel() , m_bBindMasked(true) #endif { -#ifdef _WIN32 - SecureZeroMemory((PVOID)&m_SendOverlapped, sizeof(WSAOVERLAPPED)); - m_SendOverlapped.hEvent = WSACreateEvent(); - if (m_SendOverlapped.hEvent == NULL) { - LOGC(kmlog.Error, log << CONID() << "IPE: WSACreateEvent failed with error: " << NET_ERROR); - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } -#endif #ifdef SRT_ENABLE_PKTINFO // Do the check for ancillary data buffer size, kinda assertion static const size_t CMSG_MAX_SPACE = sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6); @@ -165,12 +157,7 @@ srt::CChannel::CChannel() #endif } -srt::CChannel::~CChannel() -{ -#ifdef _WIN32 - WSACloseEvent(m_SendOverlapped.hEvent); -#endif -} +srt::CChannel::~CChannel() {} void srt::CChannel::createSocket(int family) { @@ -789,32 +776,18 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #else DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); int addrsize = addr.size(); + WSAOVERLAPPED overlapped; + SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED)); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &m_SendOverlapped, NULL); - - if (res == SOCKET_ERROR) + if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING) { - if (NET_ERROR == WSA_IO_PENDING) - { - DWORD res_wait = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); - if (res_wait == WAIT_FAILED) - { - LOGC(kslog.Warn, log << "CChannel::WSAWaitForMultipleEvents: failed with " << NET_ERROR); - res = -1; - } - else - { - DWORD dwFlags = 0; - const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &m_SendOverlapped, &size, false, &dwFlags); - res = bCompleted ? 0 : -1; - } - } - else - { - LOGC(kmlog.Error, log << CONID() << "WSASendTo failed with error: " << NET_ERROR); - } + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags); + WSACloseEvent(overlapped.hEvent); + res = bCompleted ? 0 : -1; } - WSAResetEvent(m_SendOverlapped.hEvent); + res = (0 == res) ? size : -1; #endif diff --git a/srtcore/channel.h b/srtcore/channel.h index e09b13fd9..e12310001 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -169,9 +169,6 @@ class CChannel private: UDPSOCKET m_iSocket; // socket descriptor -#ifdef _WIN32 - mutable WSAOVERLAPPED m_SendOverlapped; -#endif // Mutable because when querying original settings // this comprises the cache for extracted values, From fd4084f0f9e660c6568f146e84d810fed0ed6f3d Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 16 Apr 2024 16:16:16 +0200 Subject: [PATCH 36/85] [core] Fixed thread safety using WSAOVERLAPPED in WSASendTo. The lpOverlapped parameter must be valid for the duration of the overlapped operation. If multiple I/O operations are simultaneously outstanding, each must reference a separate WSAOVERLAPPED structure. Resolves #973, #2632, #2834, #2838. Co-authored-by: Jiangjie Gao --- srtcore/channel.cpp | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 2fd1b3805..0a4e1e318 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -774,18 +774,59 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka const int res = (int)::sendmsg(m_iSocket, &mh, 0); #else - DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); - int addrsize = addr.size(); + class WSAEventRef + { + public: + WSAEventRef() + : e(::WSACreateEvent()) + { + } + ~WSAEventRef() + { + ::WSACloseEvent(e); + e = NULL; + } + void reset() + { + ::WSAResetEvent(e); + } + WSAEVENT Handle() + { + return e; + } + + private: + WSAEVENT e; + }; +#if !defined(__MINGW32__) && defined(ENABLE_CXX11) + thread_local WSAEventRef lEvent; +#else + WSAEventRef lEvent; +#endif WSAOVERLAPPED overlapped; - SecureZeroMemory((PVOID)&overlapped, sizeof(WSAOVERLAPPED)); + ::SecureZeroMemory(&overlapped, sizeof(overlapped)); + overlapped.hEvent = lEvent.Handle(); + + DWORD size = (DWORD)(packet.m_PacketVector[0].size() + packet.m_PacketVector[1].size()); + int addrsize = addr.size(); int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); - if (res == SOCKET_ERROR && NET_ERROR == WSA_IO_PENDING) + if (res == SOCKET_ERROR) { - DWORD dwFlags = 0; - const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, true, &dwFlags); - WSACloseEvent(overlapped.hEvent); - res = bCompleted ? 0 : -1; + if (NET_ERROR == WSA_IO_PENDING) + { + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, TRUE, &dwFlags); + if (bCompleted) + res = 0; + else + LOGC(kslog.Warn, log << "CChannel::sendto call on ::WSAGetOverlappedResult failed with error: " << NET_ERROR); + lEvent.reset(); + } + else + { + LOGC(kmlog.Error, log << CONID() << "WSASendTo failed with error: " << NET_ERROR); + } } res = (0 == res) ? size : -1; From cf132005044232689cdc890a266b976e74333ca6 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 19 Apr 2024 09:51:53 +0200 Subject: [PATCH 37/85] [core] Minor connection logging improvement (#2930). Also downgraded some logs from 'note' level to 'debug'. --- srtcore/api.cpp | 3 +++ srtcore/core.cpp | 13 +++++++------ srtcore/queue.cpp | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d3e8af887..56c581fec 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -656,6 +656,9 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, HLOGC(cnlog.Debug, log << "newConnection: mapping peer " << ns->m_PeerID << " to that socket (" << ns->m_SocketID << ")"); m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); + + LOGC(cnlog.Note, log << "@" << ns->m_SocketID << " connection on listener @" << listen + << " (" << ns->m_SelfAddr.str() << ") from peer @" << ns->m_PeerID << " (" << peer.str() << ")"); } catch (...) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 2e6551423..4a8ce550e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2101,9 +2101,9 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint return SRT_CMD_NONE; } - LOGC(cnlog.Note, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] - << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen + << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] + << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -4968,8 +4968,9 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, } */ - - LOGC(cnlog.Note, log << CONID() << "Connection established to: " << m_PeerAddr.str()); + + LOGC(cnlog.Note, log << CONID() << "Connection established from (" + << m_SourceAddr.str() << ") to peer @" << m_PeerID << " (" << m_PeerAddr.str() << ")"); return CONN_ACCEPT; } @@ -11328,7 +11329,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) } } } - LOGC(cnlog.Note, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + LOGC(cnlog.Debug, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); return RejectReasonForURQ(hs.m_iReqType); } diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 345151b4e..8ad27217c 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1407,7 +1407,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, ScopedLock cg(m_LSLock); if (m_pListener) { - LOGC(cnlog.Note, log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); + LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << m_pListener->socketID()); listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); // This function does return a code, but it's hard to say as to whether @@ -1426,8 +1426,8 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, if (have_listener) // That is, the above block with m_pListener->processConnectRequest was executed { - LOGC(cnlog.Note, - log << CONID() << "Listener managed the connection request from: " << addr.str() + LOGC(cnlog.Debug, + log << CONID() << "Listener got the connection request from: " << addr.str() << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } From f6c231588da17f5bf91f82be7122664d072a93cc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:09:44 +0200 Subject: [PATCH 38/85] [core] Fixed stats counting packets dropped by a group (#2934). Also fixed m_iAvgPayloadSize initialization. --- srtcore/group.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 1539245a0..52b31d29f 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -259,6 +259,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_uOPT_MinStabilityTimeout_us(1000 * CSrtConfig::COMM_DEF_MIN_STABILITY_TIMEOUT_MS) // -1 = "undefined"; will become defined with first added socket , m_iMaxPayloadSize(-1) + , m_iAvgPayloadSize(-1) , m_bSynRecving(true) , m_bSynSending(true) , m_bTsbPd(true) @@ -2309,6 +2310,19 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } fillGroupData((w_mc), w_mc); + // TODO: What if a drop happens before the very first packet was read? Maybe set to ISN? + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + const int32_t iNumDropped = (CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; + if (iNumDropped > 0) + { + m_stats.recvDrop.count(stats::BytesPackets(iNumDropped * static_cast(avgRcvPacketSize()), iNumDropped)); + LOGC(grlog.Warn, + log << "@" << m_GroupID << " GROUP RCV-DROPPED " << iNumDropped << " packet(s): seqno %" + << m_RcvBaseSeqNo << " to %" << w_mc.pktseq); + } + } + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": Update m_RcvBaseSeqNo: %" << m_RcvBaseSeqNo << " -> %" << w_mc.pktseq); m_RcvBaseSeqNo = w_mc.pktseq; From 4f925fbca586c0d39bf80febd9d8315a8092f1f7 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:11:25 +0200 Subject: [PATCH 39/85] [core] Stats: do not count discarded packets as dropped (#2932). Valid for a broadcast group member dropping existing packets from the RCV buffer because they were read from another member. Co-authored-by: Sektor van Skijlen --- srtcore/buffer_rcv.cpp | 18 +++++++++++++----- srtcore/buffer_rcv.h | 4 ++-- srtcore/core.cpp | 19 +++++++++++++------ srtcore/core.h | 9 ++++++++- srtcore/group.cpp | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index fb389e4be..2ec42487d 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -206,7 +206,7 @@ int CRcvBuffer::insert(CUnit* unit) return 0; } -int CRcvBuffer::dropUpTo(int32_t seqno) +std::pair CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); @@ -215,16 +215,23 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); - return 0; + return std::make_pair(0, 0); } m_iMaxPosOff -= len; if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; + int iNumDropped = 0; // Number of dropped packets that were missing. + int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. while (len > 0) { + // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. + // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. + if (m_entries[m_iStartPos].status == EntryState_Avail) + ++iNumDiscarded; + else if (m_entries[m_iStartPos].status == EntryState_Empty) + ++iNumDropped; dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); @@ -246,7 +253,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) updateFirstReadableOutOfOrder(); - return iDropCnt; + return std::make_pair(iNumDropped, iNumDiscarded); } int CRcvBuffer::dropAll() @@ -255,7 +262,8 @@ int CRcvBuffer::dropAll() return 0; const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - return dropUpTo(end_seqno); + const std::pair numDropped = dropUpTo(end_seqno); + return numDropped.first + numDropped.second; } int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index d664373f5..3c60be21d 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -66,8 +66,8 @@ class CRcvBuffer /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number - /// @return number of dropped packets. - int dropUpTo(int32_t seqno); + /// @return number of dropped (missing) and discarded (available) packets as a pair(dropped, discarded). + std::pair dropUpTo(int32_t seqno); /// @brief Drop all the packets in the receiver buffer. /// The starting position and seqno are shifted right after the last packet in the buffer. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4a8ce550e..6752d7e9f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5576,7 +5576,7 @@ void * srt::CUDT::tsbpd(void* param) return NULL; } -int srt::CUDT::rcvDropTooLateUpTo(int seqno) +int srt::CUDT::rcvDropTooLateUpTo(int seqno, DropReason reason) { // Make sure that it would not drop over m_iRcvCurrSeqNo, which may break senders. if (CSeqNo::seqcmp(seqno, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) @@ -5584,16 +5584,22 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) dropFromLossLists(SRT_SEQNO_NONE, CSeqNo::decseq(seqno)); - const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); - if (iDropCnt > 0) + const std::pair iDropDiscardedPkts = m_pRcvBuffer->dropUpTo(seqno); + const int iDropCnt = iDropDiscardedPkts.first; + const int iDiscardedCnt = iDropDiscardedPkts.second; + const int iDropCntTotal = iDropCnt + iDiscardedCnt; + + // In case of DROP_TOO_LATE discarded packets should also be counted because they are not read from another member socket. + const int iDropStatCnt = (reason == DROP_DISCARD) ? iDropCnt : iDropCntTotal; + if (iDropStatCnt > 0) { enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropStatCnt * avgpayloadsz, (uint32_t)iDropStatCnt)); leaveCS(m_StatsLock); } - return iDropCnt; + return iDropCntTotal; } void srt::CUDT::setInitialRcvSeq(int32_t isn) @@ -7835,7 +7841,7 @@ void srt::CUDT::dropToGroupRecvBase() return; ScopedLock lck(m_RcvBufferLock); - int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base)); + const int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base), DROP_DISCARD); if (cnt > 0) { HLOGC(grlog.Debug, @@ -8063,6 +8069,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) string reason; // just for "a reason" of giving particular % for ACK #if ENABLE_BONDING + // TODO: The group drops other members upon reading, maybe no longer needed here? dropToGroupRecvBase(); #endif diff --git a/srtcore/core.h b/srtcore/core.h index 10746c8c9..73b053197 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -755,11 +755,18 @@ class CUDT // TSBPD thread main function. static void* tsbpd(void* param); + enum DropReason + { + DROP_TOO_LATE, //< Drop to keep up to the live pace (TLPKTDROP). + DROP_DISCARD //< Drop because another group member already provided these packets. + }; + /// Drop too late packets (receiver side). Update loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. + /// @param reason A reason for dropping (see @a DropReason). /// @return The number of packets dropped. - int rcvDropTooLateUpTo(int seqno); + int rcvDropTooLateUpTo(int seqno, DropReason reason = DROP_TOO_LATE); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 52b31d29f..282e8f25a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2338,7 +2338,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo), CUDT::DROP_DISCARD); if (cnt > 0) { HLOGC(grlog.Debug, From 973be583a30c76c250744c6eea7ae93982715072 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 10:13:35 +0200 Subject: [PATCH 40/85] [core] Removed group syncing when sending control ACK (#2935). --- srtcore/core.cpp | 39 --------------------------------------- srtcore/core.h | 6 ------ 2 files changed, 45 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6752d7e9f..d12628220 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7818,40 +7818,6 @@ void srt::CUDT::releaseSynch() leaveCS(m_RecvLock); } - -#if ENABLE_BONDING -void srt::CUDT::dropToGroupRecvBase() -{ - int32_t group_recv_base = SRT_SEQNO_NONE; - if (m_parent->m_GroupOf) - { - // Check is first done before locking to avoid unnecessary - // mutex locking. The condition for this field is that it - // can be either never set, already reset, or ever set - // and possibly dangling. The re-check after lock eliminates - // the dangling case. - ScopedLock glock (uglobal().m_GlobControlLock); - - // Note that getRcvBaseSeqNo() will lock m_GroupOf->m_GroupLock, - // but this is an intended order. - if (m_parent->m_GroupOf) - group_recv_base = m_parent->m_GroupOf->getRcvBaseSeqNo(); - } - if (group_recv_base == SRT_SEQNO_NONE) - return; - - ScopedLock lck(m_RcvBufferLock); - const int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base), DROP_DISCARD); - if (cnt > 0) - { - HLOGC(grlog.Debug, - log << CONID() << "dropToGroupRecvBase: dropped " << cnt << " packets before ACK: group_recv_base=" - << group_recv_base << " m_iRcvLastAck=" << m_iRcvLastAck - << " m_iRcvCurrSeqNo=" << m_iRcvCurrSeqNo << " m_bTsbPd=" << m_bTsbPd); - } -} -#endif - namespace srt { #if ENABLE_HEAVY_LOGGING static void DebugAck(string hdr, int prev, int ack) @@ -8068,11 +8034,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #endif string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING - // TODO: The group drops other members upon reading, maybe no longer needed here? - dropToGroupRecvBase(); -#endif - // The TSBPD thread may change the first lost sequence record (TLPKTDROP). // To avoid it the m_RcvBufferLock has to be acquired. UniqueLock bufflock(m_RcvBufferLock); diff --git a/srtcore/core.h b/srtcore/core.h index 73b053197..983704682 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1156,12 +1156,6 @@ class CUDT static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0); -#if ENABLE_BONDING - /// @brief Drop packets in the recv buffer behind group_recv_base. - /// Updates m_iRcvLastSkipAck if it's behind group_recv_base. - void dropToGroupRecvBase(); -#endif - void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival); From 882dff96e1800b1c2f286e23aaf28bf22c7342ec Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 24 Apr 2024 16:18:52 +0200 Subject: [PATCH 41/85] [core] Minor internal logging format changes (#2939). Also renamed CUDT::m_bTsbPdAckWakeup. Added some function to the CRcvBuffer. Co-authored-by: Sektor van Skijlen --- srtcore/buffer_rcv.h | 19 +++++++++++++--- srtcore/core.cpp | 54 ++++++++++++++++++++++++++++++++------------ srtcore/core.h | 12 ++++++++-- srtcore/queue.cpp | 4 ++-- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 3c60be21d..f783ac2a2 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -200,6 +200,20 @@ class CRcvBuffer return (m_iMaxPosOff == 0); } + /// Returns the currently used number of cells, including + /// gaps with empty cells, or in other words, the distance + /// between the initial position and the youngest received packet. + size_t size() const + { + return m_iMaxPosOff; + } + + // Returns true if the buffer is full. Requires locking. + bool full() const + { + return size() == capacity(); + } + /// Return buffer capacity. /// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer". /// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously. @@ -333,9 +347,8 @@ class CRcvBuffer EntryStatus status; }; - //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - - FixedArray m_entries; + typedef FixedArray entries_t; + entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d12628220..a0c6e4dbd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -298,7 +298,7 @@ void srt::CUDT::construct() m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bTsbPdNeedsWakeup = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; m_bBufferWasFull = false; @@ -5405,7 +5405,7 @@ void * srt::CUDT::tsbpd(void* param) CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond); CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker()); - self->m_bTsbPdAckWakeup = true; + self->m_bTsbPdNeedsWakeup = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5425,6 +5425,21 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; +#if ENABLE_HEAVY_LOGGING + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: NO PACKETS"); + } + else + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + } +#endif + if (!self->m_bTLPktDrop) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5470,8 +5485,8 @@ void * srt::CUDT::tsbpd(void* param) if (rxready) { HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " + << FormatDuration(steady_clock::now() - info.tsbpd_time) << ")"); /* * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available @@ -5534,6 +5549,8 @@ void * srt::CUDT::tsbpd(void* param) if (self->m_bClosing) break; + SRT_ATR_UNUSED bool bWokeUpOnSignal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5541,12 +5558,12 @@ void * srt::CUDT::tsbpd(void* param) * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. */ - self->m_bTsbPdAckWakeup = false; + self->m_bTsbPdNeedsWakeup = false; HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + bWokeUpOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5563,13 +5580,15 @@ void * srt::CUDT::tsbpd(void* param) * - Closing the connection */ HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; + self->m_bTsbPdNeedsWakeup = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: WAKE UP [" << (bWokeUpOnSignal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); @@ -6951,6 +6970,12 @@ bool srt::CUDT::isRcvBufferReadyNoLock() const return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); } +bool srt::CUDT::isRcvBufferFull() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->full(); +} + // int by_exception: accepts values of CUDTUnited::ErrorHandling: // - 0 - by return value // - 1 - by exception @@ -7738,8 +7763,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) m_iCongestionWindow = cgwindow; #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, - log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us (" - << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << cgwindow); #endif } @@ -8141,7 +8166,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) /* Newly acknowledged data, signal TsbPD thread */ CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdAckWakeup) + if (m_bTsbPdNeedsWakeup) tslcc.notify_one(); } else @@ -8204,7 +8229,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else if (!bNeedFullAck) { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr(" << reason << ") %" + << ack << " <% last %" << m_iRcvLastAck); return nbsent; } diff --git a/srtcore/core.h b/srtcore/core.h index 983704682..e24bd8152 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -327,6 +327,7 @@ class CUDT #endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + SRT_ATTR_REQUIRES(m_RecvAckLock) int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } @@ -388,6 +389,7 @@ class CUDT /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @returns The number of packets in flight belonging to the interval [0; ...) + SRT_ATTR_REQUIRES(m_RecvAckLock) int32_t getFlightSpan() const { return getFlightSpan(m_iSndLastAck, m_iSndCurrSeqNo); @@ -697,6 +699,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_REQUIRES(m_RecvAckLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) @@ -752,6 +756,9 @@ class CUDT SRT_ATTR_REQUIRES(m_RcvBufferLock) bool isRcvBufferReadyNoLock() const; + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + bool isRcvBufferFull() const; + // TSBPD thread main function. static void* tsbpd(void* param); @@ -987,7 +994,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1136,7 +1143,8 @@ class CUDT /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - /// Get the packet's TSBPD time. + /// Get the packet's TSBPD time - + /// the time when it is passed to the reading application. /// The @a grp passed by void* is not used yet /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8ad27217c..98999a81f 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1092,8 +1092,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << " " << FormatDuration(tsNow - tsLastReq) + << " passed since last connection request."); continue; } From f99ce57760aefb2cd21af275e8de0fff9132c6e3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 24 Apr 2024 16:27:03 +0200 Subject: [PATCH 42/85] [core] Fixed group RCV drop sequence range log. A follow-up for #2934. --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 282e8f25a..f0392e151 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2319,7 +2319,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) m_stats.recvDrop.count(stats::BytesPackets(iNumDropped * static_cast(avgRcvPacketSize()), iNumDropped)); LOGC(grlog.Warn, log << "@" << m_GroupID << " GROUP RCV-DROPPED " << iNumDropped << " packet(s): seqno %" - << m_RcvBaseSeqNo << " to %" << w_mc.pktseq); + << CSeqNo::incseq(m_RcvBaseSeqNo) << " to %" << CSeqNo::decseq(w_mc.pktseq)); } } From 11f081c984d6e6f1c6a8e2d2ce6df1f5323866c0 Mon Sep 17 00:00:00 2001 From: Aleksei Minaev <1472675+zulkis@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:44:33 +0200 Subject: [PATCH 43/85] [build] Add visionOS support --- CMakeLists.txt | 3 +- scripts/visionOS.cmake | 171 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 scripts/visionOS.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c30e49ac..47d9b88df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSNAME_LC) set_if(DARWIN (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "iOS") OR (${CMAKE_SYSTEM_NAME} MATCHES "tvOS") - OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS")) + OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS") + OR (${CMAKE_SYSTEM_NAME} MATCHES "visionOS")) set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) diff --git a/scripts/visionOS.cmake b/scripts/visionOS.cmake new file mode 100644 index 000000000..0f0f60b69 --- /dev/null +++ b/scripts/visionOS.cmake @@ -0,0 +1,171 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for VISIONOS development + +# Options: +# +# VISION_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 +# This decides if SDKS will be selected from the XROS.platform or XRSimulator.platform folders +# OS - the default, used to build for Vision Pro physical device, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# VISIONOS_ARCH = arm64 (default for OS), x86_64 (addiitonal support for SIMULATOR64) +# +# CMAKE_VISIONOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the VISIONOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_VISIONOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_VISIONOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_VISIONOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version +# + +# Standard settings +set (CMAKE_SYSTEM_NAME Darwin) +set (CMAKE_SYSTEM_VERSION 1) +set (UNIX True) +set (APPLE True) +set (VISIONOS True) + +# Required as of cmake 2.8.10 +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for visionOs" FORCE) + +# Determine the cmake host system version so we know where to find the visionOS SDKs +find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) +if (CMAKE_UNAME) + execute_process(COMMAND uname -r + OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) + string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") +endif (CMAKE_UNAME) + + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG) + set(VISIONOS_DEBUG_OPTIONS "-glldb -gmodules") +else() + set(VISIONOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden") +endif() + +set (CMAKE_C_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") +set (CMAKE_CXX_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") + +set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}") + + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +if (NOT DEFINED PKG_CONFIG_EXECUTABLE) + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) + if (DEFINED PKG_CONFIG_EXECUTABLE) + execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING) + endif(DEFINED PKG_CONFIG_EXECUTABLE) +endif(NOT DEFINED PKG_CONFIG_EXECUTABLE) + + +# fffio Specify path to install shared library on device +set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks") +set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE) + +# Setup visionOS platform unless specified manually with VISIONOS_PLATFORM +if (NOT DEFINED VISIONOS_PLATFORM) + set (VISIONOS_PLATFORM "OS") +endif (NOT DEFINED VISIONOS_PLATFORM) +set (VISIONOS_PLATFORM ${VISIONOS_PLATFORM} CACHE STRING "Type of visionOS Platform") + +# Check the platform selection and setup for developer root +if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_PLATFORM_LOCATION "XROS.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xros") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +else (${VISIONOS_PLATFORM} STREQUAL OS) + message (FATAL_ERROR "Unsupported VISIONOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${VISIONOS_PLATFORM} STREQUAL OS) + +# Setup visionOS developer location unless specified manually with CMAKE_VISIONOS_DEVELOPER_ROOT +if (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) + execute_process(COMMAND /usr/bin/xcode-select -print-path + OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) + string(STRIP "${CMAKE_XCODE_DEVELOPER_DIR}" CMAKE_XCODE_DEVELOPER_DIR) # FIXED: remove new line character, otherwise it complain no visionOS SDK's found in default search path + set (CMAKE_VISIONOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${VISIONOS_PLATFORM_LOCATION}/Developer") +endif (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) +set (CMAKE_VISIONOS_DEVELOPER_ROOT ${CMAKE_VISIONOS_DEVELOPER_ROOT} CACHE PATH "Location of visionOS Platform") + +# Find and use the most recent visionOS sdk unless specified manually with CMAKE_VISIONOS_SDK_ROOT +if (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) + file (GLOB _CMAKE_VISIONOS_SDKS "${CMAKE_VISIONOS_DEVELOPER_ROOT}/SDKs/*") + if (_CMAKE_VISIONOS_SDKS) + list (SORT _CMAKE_VISIONOS_SDKS) + list (REVERSE _CMAKE_VISIONOS_SDKS) + list (GET _CMAKE_VISIONOS_SDKS 0 CMAKE_VISIONOS_SDK_ROOT) + else (_CMAKE_VISIONOS_SDKS) + message (FATAL_ERROR "No visionOS SDK's found in default search path ${CMAKE_VISIONOS_DEVELOPER_ROOT}. Manually set CMAKE_VISIONOS_SDK_ROOT or install the visionOS SDK.") + endif (_CMAKE_VISIONOS_SDKS) + message (STATUS "Toolchain using default visionOS SDK: ${CMAKE_VISIONOS_SDK_ROOT}") +endif (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) +set (CMAKE_VISIONOS_SDK_ROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Location of the selected visionOS SDK") + +# Set the sysroot default to the most recent SDK +set (CMAKE_OSX_SYSROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Sysroot used for visionOS support") + +# set the architecture for visionOS +if (NOT DEFINED VISIONOS_ARCH) + if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (VISIONOS_ARCH x86_64) + endif (${VISIONOS_PLATFORM} STREQUAL OS) +endif(NOT DEFINED VISIONOS_ARCH) +set (CMAKE_OSX_ARCHITECTURES ${VISIONOS_ARCH} CACHE STRING "Build architecture for visionOS") + +# Set the find root to the visionOS developer roots and to user defined paths +set (CMAKE_FIND_ROOT_PATH ${CMAKE_VISIONOS_DEVELOPER_ROOT} ${CMAKE_VISIONOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "visionOS find search path root") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_VISIONOS_SDK_ROOT}/Developer/Library/Frameworks +) + +# only search the visionOS sdks, not the remainder of the host filesystem (except for programs, so that we can still find Python if needed) +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + From d31d83e2ce85bdea5eae84d4f255d4956d72e641 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Apr 2024 11:34:20 +0200 Subject: [PATCH 44/85] [core] Group set the RCV base seqno using peer ISN. --- srtcore/group.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f0392e151..b37504874 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -870,18 +870,9 @@ void CUDTGroup::syncWithSocket(const CUDT& core, const HandshakeSide side) set_currentSchedSequence(core.ISN()); } - // XXX - // Might need further investigation as to whether this isn't - // wrong for some cases. By having this -1 here the value will be - // laziliy set from the first reading one. It is believed that - // it covers all possible scenarios, that is: - // - // - no readers - no problem! - // - have some readers and a new is attached - this is set already - // - connect multiple links, but none has read yet - you'll be the first. - // - // Previous implementation used setting to: core.m_iPeerISN - resetInitialRxSequence(); + // Only set if was not initialized to avoid problems on a running connection. + if (m_RcvBaseSeqNo == SRT_SEQNO_NONE) + m_RcvBaseSeqNo = CSeqNo::decseq(core.m_iPeerISN); // Get the latency (possibly fixed against the opposite side) // from the first socket (core.m_iTsbPdDelay_ms), @@ -2310,7 +2301,8 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } fillGroupData((w_mc), w_mc); - // TODO: What if a drop happens before the very first packet was read? Maybe set to ISN? + // m_RcvBaseSeqNo is expected to be set to the PeerISN with the first connected member, + // so a packet drop at the start should also be detected by this condition. if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { const int32_t iNumDropped = (CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; From ceb4cca3f453061e7ebda7835a7407d5cadf951b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 22 Apr 2024 18:25:26 +0200 Subject: [PATCH 45/85] [core] Fixed group recv read-ready check. --- srtcore/group.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b37504874..0927d085a 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2040,10 +2040,14 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& } else { - // No read-readiness reported by epoll, but probably missed or not yet handled - // as the receiver buffer is read-ready. + // No read-readiness reported by epoll, but can be missed or not yet handled + // while the receiver buffer is in fact read-ready. ScopedLock lg(sock->core().m_RcvBufferLock); - if (sock->core().m_pRcvBuffer && sock->core().m_pRcvBuffer->isRcvDataReady()) + if (!sock->core().m_pRcvBuffer) + continue; + // Checking for the next packet in the RCV buffer is safer that isReadReady(tnow). + const CRcvBuffer::PacketInfo info = sock->core().m_pRcvBuffer->getFirstValidPacketInfo(); + if (info.seqno != SRT_SEQNO_NONE && !info.seq_gap) readReady.push_back(sock); } } @@ -2213,6 +2217,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } // Find the first readable packet among all member sockets. + steady_clock::time_point tnow = steady_clock::now(); CUDTSocket* socketToRead = NULL; CRcvBuffer::PacketInfo infoToRead = {-1, false, time_point()}; for (vector::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) @@ -2233,7 +2238,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } const CRcvBuffer::PacketInfo info = - ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); + ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(tnow); if (info.seqno == SRT_SEQNO_NONE) { HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": Nothing to read."); @@ -2253,6 +2258,12 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { socketToRead = ps; infoToRead = info; + + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && ((CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) == 1)) + { + // We have the next packet. No need to check other read-ready sockets. + break; + } } } From 57a4d9f3926d8ecf06fee1b916eca55ccd0eae5b Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 25 Apr 2024 12:06:04 +0200 Subject: [PATCH 46/85] [core] Fixed time base sync in a group. --- srtcore/core.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a0c6e4dbd..6aec48b94 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8721,16 +8721,15 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // srt_recvfile (which doesn't make any sense), you'll have a deadlock. if (m_config.bDriftTracer) { - const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); #if ENABLE_BONDING - if (drift_updated && m_parent->m_GroupOf) - { - ScopedLock glock(uglobal().m_GlobControlLock); - if (m_parent->m_GroupOf) - { - m_parent->m_GroupOf->synchronizeDrift(this); - } - } + ScopedLock glock(uglobal().m_GlobControlLock); + const bool drift_updated = +#endif + m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); + +#if ENABLE_BONDING + if (drift_updated) + m_parent->m_GroupOf->synchronizeDrift(this); #endif } From 07859c85074f8abf5f13b2fef59a6dd1d5c25cc3 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 26 Apr 2024 11:44:42 +0200 Subject: [PATCH 47/85] [core] Fixed group synchronization of accepted sockets. Fixes #2941. --- srtcore/core.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6aec48b94..bc4e0d3de 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5755,14 +5755,6 @@ void srt::CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: setting up data according to handshake"); -#if ENABLE_BONDING - // Keep the group alive for the lifetime of this function, - // and do it BEFORE acquiring m_ConnectionLock to avoid - // lock inversion. - // This will check if a socket belongs to a group and if so - // it will remember this group and keep it alive here. - CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); -#endif ScopedLock cg(m_ConnectionLock); @@ -5850,6 +5842,16 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } +#if ENABLE_BONDING + // The socket and the group are only linked to each other after interpretSrtHandshake(..) has been called. + // Keep the group alive for the lifetime of this function, + // and do it BEFORE acquiring m_ConnectionLock to avoid + // lock inversion. + // This will check if a socket belongs to a group and if so + // it will remember this group and keep it alive here. + CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); +#endif + if (!prepareBuffers(NULL)) { HLOGC(cnlog.Debug, From 38a3a165a1af92f6423de8c97646de07cba5e120 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 30 Apr 2024 11:30:19 +0200 Subject: [PATCH 48/85] [core] Fixed checking the m_GroupOf is not NULL. A follow-up fix for #2938. --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index bc4e0d3de..1612830e7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8730,7 +8730,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); #if ENABLE_BONDING - if (drift_updated) + if (drift_updated && m_parent->m_GroupOf) m_parent->m_GroupOf->synchronizeDrift(this); #endif } From ebe2c7118fe23fcb171170913f59e9e1f0641d9b Mon Sep 17 00:00:00 2001 From: kageds <65413014+kageds@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:37:07 +0100 Subject: [PATCH 49/85] [core] Fix build issues with ENFORCE_SRT_DEBUG_BONDING_STATES (#2948). --- srtcore/group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 0927d085a..3e57ef5df 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2574,7 +2574,7 @@ class StabilityTracer str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); } const std::string fname = "stability_trace_" + str_tnow + ".csv"; - m_fout.open(fname, std::ofstream::out); + m_fout.open(fname.c_str(), std::ofstream::out); if (!m_fout) std::cerr << "IPE: Failed to open " << fname << "!!!\n"; From 72303d7934f9c6b1cbe23c438672f0eba0f318cb Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 12 Jun 2024 15:55:53 +0200 Subject: [PATCH 50/85] [core] Fixed bug: srt_accept failure may make accepted socket leak (#1884). Added unit test. --- srtcore/api.cpp | 43 ++++++++++++-------- srtcore/api.h | 2 +- test/test_connection_timeout.cpp | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 56c581fec..ca26600d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -766,7 +766,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, enterCS(ls->m_AcceptLock); try { - ls->m_QueuedSockets.insert(ns->m_SocketID); + ls->m_QueuedSockets[ns->m_SocketID] = ns->m_PeerAddr; } catch (...) { @@ -1110,8 +1110,22 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int } else if (ls->m_QueuedSockets.size() > 0) { - set::iterator b = ls->m_QueuedSockets.begin(); - u = *b; + map::iterator b = ls->m_QueuedSockets.begin(); + + if (pw_addr != NULL && pw_addrlen != NULL) + { + // Check if the length of the buffer to fill the name in + // was large enough. + const int len = b->second.size(); + if (*pw_addrlen < len) + { + // In case when the address cannot be rewritten, + // DO NOT accept, but leave the socket in the queue. + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + u = b->first; ls->m_QueuedSockets.erase(b); accepted = true; } @@ -1182,14 +1196,8 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int if (pw_addr != NULL && pw_addrlen != NULL) { - // Check if the length of the buffer to fill the name in - // was large enough. - const int len = s->m_PeerAddr.size(); - if (*pw_addrlen < len) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memcpy((pw_addr), &s->m_PeerAddr, len); - *pw_addrlen = len; + memcpy((pw_addr), s->m_PeerAddr.get(), s->m_PeerAddr.size()); + *pw_addrlen = s->m_PeerAddr.size(); } return u; @@ -2751,23 +2759,24 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // if it is a listener, close all un-accepted sockets in its queue // and remove them later - for (set::iterator q = s->m_QueuedSockets.begin(); q != s->m_QueuedSockets.end(); ++q) + for (map::iterator q = s->m_QueuedSockets.begin(); + q != s->m_QueuedSockets.end(); ++ q) { - sockets_t::iterator si = m_Sockets.find(*q); + sockets_t::iterator si = m_Sockets.find(q->first); if (si == m_Sockets.end()) { // gone in the meantime LOGC(smlog.Error, - log << "removeSocket: IPE? socket @" << (*q) << " being queued for listener socket @" - << s->m_SocketID << " is GONE in the meantime ???"); + log << "removeSocket: IPE? socket @" << (q->first) << " being queued for listener socket @" + << s->m_SocketID << " is GONE in the meantime ???"); continue; } CUDTSocket* as = si->second; as->breakSocket_LOCKED(); - m_ClosedSockets[*q] = as; - m_Sockets.erase(*q); + m_ClosedSockets[q->first] = as; + m_Sockets.erase(q->first); } } diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..fddbfc294 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -151,7 +151,7 @@ class CUDTSocket CUDT m_UDT; //< internal SRT socket logic public: - std::set m_QueuedSockets; //< set of connections waiting for accept() + std::map m_QueuedSockets; //< set of connections waiting for accept() sync::Condition m_AcceptCond; //< used to block "accept" call sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 0b8bb7874..dca7595b8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "test_env.h" @@ -12,6 +13,7 @@ typedef int SOCKET; #include"platform_sys.h" #include "srt.h" +#include "netinet_any.h" using namespace std; @@ -204,3 +206,68 @@ TEST_F(TestConnectionTimeout, BlockingLoop) } +TEST(TestConnectionAPI, Accept) +{ + using namespace std::chrono; + using namespace srt; + + srt_startup(); + + const SRTSOCKET caller_sock = srt_create_socket(); + const SRTSOCKET listener_sock = srt_create_socket(); + + const int eidl = srt_epoll_create(); + const int eidc = srt_epoll_create(); + const int ev_conn = SRT_EPOLL_OUT | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidc, caller_sock, &ev_conn); + const int ev_acp = SRT_EPOLL_IN | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidl, listener_sock, &ev_acp); + + sockaddr_any sa = srt::CreateAddr("localhost", 5555, AF_INET); + + ASSERT_NE(srt_bind(listener_sock, sa.get(), sa.size()), -1); + ASSERT_NE(srt_listen(listener_sock, 1), -1); + + // Set non-blocking mode so that you can wait for readiness + bool no = false; + srt_setsockflag(caller_sock, SRTO_RCVSYN, &no, sizeof no); + srt_setsockflag(listener_sock, SRTO_RCVSYN, &no, sizeof no); + + srt_connect(caller_sock, sa.get(), sa.size()); + + SRT_EPOLL_EVENT ready[2]; + int nready = srt_epoll_uwait(eidl, ready, 2, 1000); // Wait 1s + EXPECT_EQ(nready, 1); + EXPECT_EQ(ready[0].fd, listener_sock); + // EXPECT_EQ(ready[0].events, SRT_EPOLL_IN); + + // Now call the accept function incorrectly + int size = 0; + sockaddr_storage saf; + + EXPECT_EQ(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + std::this_thread::sleep_for(seconds(1)); + + // Set correctly + size = sizeof (sockaddr_in6); + EXPECT_NE(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + // Ended up with error, but now you should also expect error on the caller side. + + // Wait 5s until you get a connection broken. + nready = srt_epoll_uwait(eidc, ready, 2, 5000); + EXPECT_EQ(nready, 1); + if (nready == 1) + { + // Do extra checks only if you know that this was returned. + EXPECT_EQ(ready[0].fd, caller_sock); + EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0); + } + srt_close(caller_sock); + srt_close(listener_sock); + + srt_cleanup(); +} + + From b7c8050aa11b4afb41b92521d25ffa75133cceeb Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 26 Jun 2024 13:10:13 +0200 Subject: [PATCH 51/85] [core] Fixed missing traceBelatedTime initialization. --- srtcore/core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1612830e7..faf9c26ab 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -929,6 +929,7 @@ void srt::CUDT::clearData() m_stats.tsLastSampleTime = steady_clock::now(); m_stats.traceReorderDistance = 0; + m_stats.traceBelatedTime = 0; m_stats.sndDuration = m_stats.m_sndDurationTotal = 0; } From 36260c395b6f65182e67ba19d776d875836c6ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 26 Jun 2024 17:38:29 +0200 Subject: [PATCH 52/85] [core] Removed settable ability for SRTO_VERSION --- srtcore/socketconfig.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index d44330f78..1c067d059 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -559,15 +559,6 @@ struct CSrtConfigSetter } }; -template<> -struct CSrtConfigSetter -{ - static void set(CSrtConfig& co, const void* optval, int optlen) - { - co.uSrtVersion = cast_optval(optval, optlen); - } -}; - template<> struct CSrtConfigSetter { @@ -971,7 +962,6 @@ int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int opt DISPATCH(SRTO_CONNTIMEO); DISPATCH(SRTO_DRIFTTRACER); DISPATCH(SRTO_LOSSMAXTTL); - DISPATCH(SRTO_VERSION); DISPATCH(SRTO_MINVERSION); DISPATCH(SRTO_STREAMID); DISPATCH(SRTO_CONGESTION); From 5e6e80b525f6bc2b96f24fbef2143a5a51e59a7c Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 26 Jun 2024 15:15:23 +0200 Subject: [PATCH 53/85] [core] Fixed broadcast group: cut failed links on partial sending success --- srtcore/group.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 3e57ef5df..d2d275bf6 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1501,7 +1501,10 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) int ercode = 0; - if (was_blocked) + // This block causes waiting for any socket to accept the payload. + // This should be done only in blocking mode and only if no other socket + // accepted the payload. + if (was_blocked && none_succeeded && m_bSynSending) { m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_OUT, false); if (!m_bSynSending) @@ -1648,6 +1651,19 @@ int CUDTGroup::sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc) throw CUDTException(major, minor, 0); } + for (vector::iterator is = sendstates.begin(); is != sendstates.end(); ++is) + { + // Here we have a situation that at least 1 link successfully sent a packet. + // All links for which sending has failed must be closed. + if (is->stat == -1) + { + // This only sets the state to the socket; the GC process should + // pick it up at the next time. + HLOGC(gslog.Debug, log << "grp/sendBroadcast: per PARTIAL SUCCESS, closing failed @" << is->id); + is->mb->ps->setBrokenClosed(); + } + } + // Now that at least one link has succeeded, update sending stats. m_stats.sent.count(len); From 0680092fe6aef1c912f8403ffaf4a2baf8e8db34 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 3 Jul 2024 14:39:42 +0200 Subject: [PATCH 54/85] [core] Removed a wrong assertion about ACK timestamp. --- srtcore/core.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index faf9c26ab..b7f2723c2 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8044,7 +8044,6 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { - SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); int nbsent = 0; int local_prevack = 0; #if ENABLE_HEAVY_LOGGING From 54c002f6ad9e3643d5d8117d32c5dada9944c151 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Jul 2024 12:27:43 +0200 Subject: [PATCH 55/85] [core] Fix TSBPD thread create/join protection. Co-authored-by: Sektor van Skijlen --- srtcore/core.cpp | 9 +++++---- srtcore/core.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b7f2723c2..83ff52f16 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9992,12 +9992,13 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) int srt::CUDT::checkLazySpawnTsbPdThread() { const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; + if (!need_tsbpd) + return 0; - if (need_tsbpd && !m_RcvTsbPdThread.joinable()) + ScopedLock lock(m_RcvTsbPdStartupLock); + if (!m_RcvTsbPdThread.joinable()) { - ScopedLock lock(m_RcvTsbPdStartupLock); - - if (m_bClosing) // Check again to protect join() in CUDT::releaseSync() + if (m_bClosing) // Check m_bClosing to protect join() in CUDT::releaseSync(). return -1; HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); diff --git a/srtcore/core.h b/srtcore/core.h index e24bd8152..3935b99d3 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -995,7 +995,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. - sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining + sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creation and joining. CallbackHolder m_cbAcceptHook; CallbackHolder m_cbConnectHook; From 84d18ec9d1ef9e1bb37133d07151d59e92b4295e Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 9 Jul 2024 17:09:34 +0200 Subject: [PATCH 56/85] [core] Added missing SRT_ATTR_GUARDED_BY(m_GlobControlLock). Removed unused m_MultiplexerLock. --- srtcore/api.cpp | 5 +---- srtcore/api.h | 20 +++++++++++++++----- srtcore/cache.h | 10 ++++++---- srtcore/core.cpp | 2 +- srtcore/group.cpp | 2 +- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ca26600d1..665593c39 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -174,8 +174,7 @@ srt::CUDTUnited::CUDTUnited() , m_GlobControlLock() , m_IDLock() , m_mMultiplexer() - , m_MultiplexerLock() - , m_pCache(NULL) + , m_pCache(new CCache) , m_bClosing(false) , m_GCStopCond() , m_InitLock() @@ -195,8 +194,6 @@ srt::CUDTUnited::CUDTUnited() setupMutex(m_GlobControlLock, "GlobControl"); setupMutex(m_IDLock, "ID"); setupMutex(m_InitLock, "Init"); - - m_pCache = new CCache; } srt::CUDTUnited::~CUDTUnited() diff --git a/srtcore/api.h b/srtcore/api.h index fddbfc294..6dbad6634 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -385,11 +385,13 @@ class CUDTUnited private: typedef std::map sockets_t; // stores all the socket structures - sockets_t m_Sockets; + SRT_ATTR_GUARDED_BY(m_GlobControlLock) + sockets_t m_Sockets; #if ENABLE_BONDING typedef std::map groups_t; - groups_t m_Groups; + SRT_ATTR_GUARDED_BY(m_GlobControlLock) + groups_t m_Groups; #endif sync::Mutex m_GlobControlLock; // used to synchronize UDT API @@ -399,6 +401,7 @@ class CUDTUnited SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one + SRT_ATTR_GUARDED_BY(m_GlobControlLock) std::map > m_PeerRec; // record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn @@ -460,11 +463,13 @@ class CUDTUnited const sockaddr_any& reqaddr, const CSrtMuxerConfig& cfgSocket); private: + SRT_ATTR_GUARDED_BY(m_GlobControlLock) std::map m_mMultiplexer; // UDP multiplexer - sync::Mutex m_MultiplexerLock; -private: - CCache* m_pCache; // UDT network information cache + /// UDT network information cache. + /// Existence is guarded by m_GlobControlLock, but the cache itself is thread-safe. + SRT_ATTR_GUARDED_BY(m_GlobControlLock) + CCache* const m_pCache; private: srt::sync::atomic m_bClosing; @@ -472,14 +477,19 @@ class CUDTUnited sync::Condition m_GCStopCond; sync::Mutex m_InitLock; + SRT_ATTR_GUARDED_BY(m_InitLock) int m_iInstanceCount; // number of startup() called by application + SRT_ATTR_GUARDED_BY(m_InitLock) bool m_bGCStatus; // if the GC thread is working (true) + SRT_ATTR_GUARDED_BY(m_InitLock) sync::CThread m_GCThread; static void* garbageCollect(void*); + SRT_ATTR_GUARDED_BY(m_GlobControlLock) sockets_t m_ClosedSockets; // temporarily store closed sockets #if ENABLE_BONDING + SRT_ATTR_GUARDED_BY(m_GlobControlLock) groups_t m_ClosedGroups; #endif diff --git a/srtcore/cache.h b/srtcore/cache.h index 47633706a..d5a037633 100644 --- a/srtcore/cache.h +++ b/srtcore/cache.h @@ -192,9 +192,11 @@ template class CCache return 0; } - /// Specify the cache size (i.e., max number of items). - /// @param [in] size max cache size. +private: + /// Specify the cache size (i.e., max number of items). + /// Private or else must be protected by a lock. + /// @param [in] size max cache size. void setSizeLimit(int size) { m_iMaxSize = size; @@ -202,8 +204,8 @@ template class CCache m_vHashPtr.resize(m_iHashSize); } - /// Clear all entries in the cache, restore to initialization state. - + /// Clear all entries in the cache, restore to initialization state. + /// Private or else must be protected by a lock. void clear() { for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 83ff52f16..b802bdec9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11946,7 +11946,7 @@ void srt::CUDT::processKeepalive(const CPacket& ctrlpkt, const time_point& tsArr if (m_parent->m_GroupOf) { // Lock GlobControlLock in order to make sure that - // the state if the socket having the group and the + // the state of the socket having the group and the // existence of the group will not be changed during // the operation. The attempt of group deletion will // have to wait until this operation completes. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d2d275bf6..d4598d7c1 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3199,7 +3199,7 @@ void CUDTGroup::send_CloseBrokenSockets(vector& w_wipeme) InvertedLock ug(m_GroupLock); // With unlocked GroupLock, we can now lock GlobControlLock. - // This is needed prevent any of them be deleted from the container + // This is needed to prevent any of them deleted from the container // at the same time. ScopedLock globlock(CUDT::uglobal().m_GlobControlLock); From bc2f48e057644ab9d057690b776a576304bb2c03 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Fri, 26 Jul 2024 16:11:23 +0200 Subject: [PATCH 57/85] [core] Put the use of m_bGCStatus under m_InitLock --- srtcore/api.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 665593c39..7eda3f2bf 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -236,6 +236,8 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) int srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); + if (m_bGCStatus) + return 1; if (m_iInstanceCount++ > 0) return 1; @@ -254,9 +256,6 @@ int srt::CUDTUnited::startup() PacketFilter::globalInit(); - if (m_bGCStatus) - return 1; - m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) @@ -3390,8 +3389,7 @@ int srt::CUDT::cleanup() SRTSOCKET srt::CUDT::socket() { - if (!uglobal().m_bGCStatus) - uglobal().startup(); + uglobal().startup(); try { @@ -3441,8 +3439,7 @@ srt::CUDTGroup& srt::CUDT::newGroup(const int type) SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) { // Doing the same lazy-startup as with srt_create_socket() - if (!uglobal().m_bGCStatus) - uglobal().startup(); + uglobal().startup(); try { From dbf5965244c978b30484fffaf5c6b8845fcdbb22 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 30 Jul 2024 09:52:49 +0200 Subject: [PATCH 58/85] [core] Added some SRT_ATTR_EXCLUDES to CUDT (#2979). --- srtcore/core.h | 9 ++++++++- srtcore/srt_attr_defs.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/srtcore/core.h b/srtcore/core.h index 3935b99d3..3d5314281 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -773,6 +773,7 @@ class CUDT /// @param seqno [in] The sequence number of the first packets following those to be dropped. /// @param reason A reason for dropping (see @a DropReason). /// @return The number of packets dropped. + SRT_ATTR_EXCLUDES(m_RcvBufferLock, m_RcvLossLock) int rcvDropTooLateUpTo(int seqno, DropReason reason = DROP_TOO_LATE); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); @@ -962,6 +963,7 @@ class CUDT bool frequentLogAllowed(size_t logid, const time_point& tnow, std::string& why); private: // Receiving related data + SRT_ATTR_GUARDED_BY(m_RcvBufferLock) CRcvBuffer* m_pRcvBuffer; //< Receiver buffer SRT_ATTR_GUARDED_BY(m_RcvLossLock) CRcvLossList* m_pRcvLossList; //< Receiver loss list @@ -992,6 +994,7 @@ class CUDT bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets bool m_bGroupTsbPd; // TSBPD should be used for GROUP RECEIVER instead + SRT_ATTR_GUARDED_BY(m_RcvTsbPdStartupLock) sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. @@ -1126,6 +1129,8 @@ class CUDT /// @retval false Nothing was extracted for sending, @a nexttime should be ignored bool packData(CPacket& packet, time_point& nexttime, sockaddr_any& src_addr); + /// Also excludes srt::CUDTUnited::m_GlobControlLock. + SRT_ATTR_EXCLUDES(m_RcvTsbPdStartupLock, m_StatsLock, m_RecvLock, m_RcvLossLock, m_RcvBufferLock) int processData(CUnit* unit); /// This function passes the incoming packet to the initial processing @@ -1149,8 +1154,10 @@ class CUDT /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); + SRT_ATTR_EXCLUDES(m_RcvTsbPdStartupLock) /// Checks and spawns the TSBPD thread if required. int checkLazySpawnTsbPdThread(); + void processClose(); /// Process the request after receiving the handshake from caller. @@ -1167,9 +1174,9 @@ class CUDT void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival); + SRT_ATTR_REQUIRES(m_RcvBufferLock) /// 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 diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 85ea9f96d..726c4a03b 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -118,7 +118,7 @@ used by SRT library internally. #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_EXCLUDES(...) // the caller must not hold the given capabilities #define SRT_ATTR_ASSERT_CAPABILITY(expr) #define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) #define SRT_ATTR_RETURN_CAPABILITY(x) @@ -179,6 +179,7 @@ used by SRT library internally. #define SRT_ATTR_TRY_ACQUIRE_SHARED(...) \ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) +// The caller must not hold the given capabilities. #define SRT_ATTR_EXCLUDES(...) \ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) From 2af87066040294ba72c0b6c0b37391cafd09fbd2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 30 Jul 2024 14:15:11 +0200 Subject: [PATCH 59/85] [docs] Added SonarCloud badge. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8b263aebb..0bf810144 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![License: MPLv2.0][license-badge]](./LICENSE) [![Latest release][release-badge]][github releases] +[![Quality Gate Status][sonarcloud-badge]][sonarcloud-project] [![codecov][codecov-badge]][codecov-project] [![Build Status Linux and macOS][travis-badge]][travis] [![Build Status Windows][appveyor-badge]][appveyor] @@ -225,6 +226,9 @@ By contributing code to the SRT project, you agree to license your contribution [ConanCenter-package]: https://repology.org/project/srt/versions [ConanCenter-badge]: https://repology.org/badge/version-for-repo/conancenter/srt.svg +[sonarcloud-project]: https://sonarcloud.io/project/overview?id=srt +[sonarcloud-badge]: https://sonarcloud.io/api/project_badges/measure?project=srt&metric=alert_status + [codecov-project]: https://codecov.io/gh/haivision/srt [codecov-badge]: https://codecov.io/gh/haivision/srt/branch/master/graph/badge.svg From 5819adefe5dff74eac9555fc672b63a7b950871f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 30 Jul 2024 14:36:27 +0200 Subject: [PATCH 60/85] [core] Improved AES GCM encryption, changed GCM IV length to 12 bytes (#2962). SRT version raised to 1.5.4. --- CMakeLists.txt | 2 +- haicrypt/cryspr.c | 78 ++++++++++++++++++++-------------------- haicrypt/haicrypt.h | 2 ++ haicrypt/hcrypt.c | 12 +++++++ haicrypt/hcrypt.h | 19 ++++++++++ haicrypt/hcrypt_ctx.h | 1 + haicrypt/hcrypt_ctx_rx.c | 2 +- haicrypt/hcrypt_ctx_tx.c | 3 +- srtcore/core.cpp | 14 ++++---- srtcore/crypto.cpp | 30 ++++++++++++---- srtcore/crypto.h | 20 ++++++----- 11 files changed, 120 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47d9b88df..6500e364b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ # cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) -set (SRT_VERSION 1.5.3) +set (SRT_VERSION 1.5.4) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts") include(haiUtil) # needed for set_version_variables diff --git a/haicrypt/cryspr.c b/haicrypt/cryspr.c index 2e3a68a59..a078aad73 100644 --- a/haicrypt/cryspr.c +++ b/haicrypt/cryspr.c @@ -17,6 +17,10 @@ written by CRYSPR/4SRT Initial implementation. *****************************************************************************/ +#ifndef _WIN32 +#include /* htonl */ +#endif + #include "hcrypt.h" #include "cryspr.h" @@ -429,6 +433,8 @@ static int crysprFallback_MsEncrypt( /* Auth tag produced by AES GCM. */ unsigned char tag[HAICRYPT_AUTHTAG_MAX]; + /* Additional authenticated data used by AES-GCM. */ + unsigned char aad[HAICRYPT_AAD_MAX]; /* * Get buffer room from the internal circular output buffer. @@ -452,33 +458,30 @@ static int crysprFallback_MsEncrypt( /* Get input packet index (in network order) */ hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); - /* - * Compute the Initial Vector - * IV (128-bit): - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | 0s | pki | ctr | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * XOR - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | nonce + - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * - * pki (32-bit): packet index - * ctr (16-bit): block counter - * nonce (112-bit): number used once (salt) - */ - hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); - if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { - const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, in_data[0].pfx, pfx_len, in_data[0].payload, in_data[0].len, + const bool old_aead = ctx->use_gcm_153; // SRT v1.5.2 to v1.5.3. + if (old_aead) + { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); + memcpy(aad, in_data[0].pfx, sizeof(aad)); + } + else + { + hcrypt_SetGcmIV((unsigned char*)&pki, ctx->salt, iv); + + for (size_t i = 0; i < sizeof(aad) / 4; ++i) + *((uint32_t*)aad + i) = htonl(*((uint32_t*)in_data[0].pfx + i)); + } + + const int iret = cryspr_cb->cryspr->aes_gcm_cipher(true, aes_key, iv, aad, sizeof(aad), in_data[0].payload, in_data[0].len, &out_msg[pfx_len], tag); if (iret) { return(iret); } } else { + hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); #if CRYSPR_HAS_AESCTR cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len, &out_msg[pfx_len]); @@ -599,28 +602,26 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, /* Get input packet index (in network order) */ hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1); - /* - * Compute the Initial Vector - * IV (128-bit): - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | 0s | pki | ctr | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * XOR - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * | nonce + - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - * - * pki (32-bit): packet index - * ctr (16-bit): block counter - * nonce (112-bit): number used once (salt) - */ - hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv); - if (ctx->mode == HCRYPT_CTX_MODE_AESGCM) { + /* Additional authenticated data used by AES-GCM. */ + unsigned char aad[HAICRYPT_AAD_MAX]; + const bool old_aead = ctx->use_gcm_153; // SRT v1.5.2 to v1.5.3. + if (old_aead) + { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); + memcpy(aad, in_data[0].pfx, sizeof(aad)); + } + else + { + hcrypt_SetGcmIV((unsigned char*)&pki, ctx->salt, iv); + + for (size_t i = 0; i < sizeof(aad) / 4; ++i) + *((uint32_t*)aad + i) = htonl(*((uint32_t*)in_data[0].pfx + i)); + } + unsigned char* tag = in_data[0].payload + in_data[0].len - HAICRYPT_AUTHTAG_MAX; - int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, in_data[0].pfx, ctx->msg_info->pfx_len, in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX, + int liret = cryspr_cb->cryspr->aes_gcm_cipher(false, aes_key, iv, aad, sizeof(aad), in_data[0].payload, in_data[0].len - HAICRYPT_AUTHTAG_MAX, out_txt, tag); if (liret) { return(liret); @@ -628,6 +629,7 @@ static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, out_len = in_data[0].len - HAICRYPT_AUTHTAG_MAX; } else { + hcrypt_SetCtrIV((unsigned char*)&pki, ctx->salt, iv); #if CRYSPR_HAS_AESCTR cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len, out_txt); diff --git a/haicrypt/haicrypt.h b/haicrypt/haicrypt.h index b6e83ad7a..da0ad3493 100644 --- a/haicrypt/haicrypt.h +++ b/haicrypt/haicrypt.h @@ -37,6 +37,7 @@ HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default crys #define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */ #define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ) #define HAICRYPT_AUTHTAG_MAX 16 /* maximum length of the auth tag (e.g. GCM) */ +#define HAICRYPT_AAD_MAX 16 /* maximum length of the additional authenticated data (GCM mode) */ #define HAICRYPT_SALT_SZ 16 @@ -96,6 +97,7 @@ typedef struct hcrypt_Session_str* HaiCrypt_Handle; int HaiCrypt_SetLogLevel(int level, int logfa); int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc); +int HaiCrypt_UpdateGcm153(HaiCrypt_Handle hhc, unsigned use_gcm_153); int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc); int HaiCrypt_Close(HaiCrypt_Handle hhc); int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p); diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c index dc3f06801..a2b81d832 100644 --- a/haicrypt/hcrypt.c +++ b/haicrypt/hcrypt.c @@ -178,6 +178,18 @@ int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc) return(0); } +int HaiCrypt_UpdateGcm153(HaiCrypt_Handle hhc, unsigned use_gcm_153) +{ + ASSERT(hhc != NULL); + hcrypt_Session* crypto = hhc; + if (!crypto) + return (-1); + + crypto->ctx_pair[0].use_gcm_153 = use_gcm_153; + crypto->ctx_pair[1].use_gcm_153 = use_gcm_153; + return (0); +} + int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg) { hcrypt_Session *crypto = (hcrypt_Session *)hhcSrc; diff --git a/haicrypt/hcrypt.h b/haicrypt/hcrypt.h index 34e744e8a..e28a29777 100644 --- a/haicrypt/hcrypt.h +++ b/haicrypt/hcrypt.h @@ -135,6 +135,25 @@ typedef struct hcrypt_Session_str { hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \ } while(0) +/* HaiCrypt-TP GCM mode IV (96-bit) - SRT 1.5.4: + * 0 1 2 3 4 5 6 7 8 9 10 11 + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | 0s | pki | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * XOR + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | nonce + + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * + * pki (32-bit): packet index + * nonce (96-bit): number used once (salt) + */ +#define hcrypt_SetGcmIV(pki, nonce, iv) do { \ + memset(&(iv)[0], 0, 96/8); \ + memcpy(&(iv)[8], (pki), HCRYPT_PKI_SZ); \ + hcrypt_XorStream(&(iv)[0], (nonce), 96/8); \ + } while(0) + #define hcrypt_XorStream(dst, strm, len) do { \ int __XORSTREAMi; \ for (__XORSTREAMi = 0 \ diff --git a/haicrypt/hcrypt_ctx.h b/haicrypt/hcrypt_ctx.h index 3a46fd40f..0d962c430 100644 --- a/haicrypt/hcrypt_ctx.h +++ b/haicrypt/hcrypt_ctx.h @@ -70,6 +70,7 @@ typedef struct tag_hcrypt_Ctx { #define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */ #define HCRYPT_CTX_MODE_AESGCM 4 /* AES GCM authenticated encryption */ unsigned mode; + bool use_gcm_153; /* AES-GCM compatibility mode (SRT v1.5.3 and earlier) */ struct { size_t key_len; diff --git a/haicrypt/hcrypt_ctx_rx.c b/haicrypt/hcrypt_ctx_rx.c index 9fcba6c30..2b67490d3 100644 --- a/haicrypt/hcrypt_ctx_rx.c +++ b/haicrypt/hcrypt_ctx_rx.c @@ -28,8 +28,8 @@ int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; } ctx->status = HCRYPT_CTX_S_INIT; - ctx->msg_info = crypto->msg_info; + ctx->use_gcm_153 = false; // Default initialization. if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { return(-1); diff --git a/haicrypt/hcrypt_ctx_tx.c b/haicrypt/hcrypt_ctx_tx.c index 9ed5ba36c..e66c9497b 100644 --- a/haicrypt/hcrypt_ctx_tx.c +++ b/haicrypt/hcrypt_ctx_tx.c @@ -35,7 +35,7 @@ int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cf ctx->mode = (cfg->flags & HAICRYPT_CFG_F_GCM) ? HCRYPT_CTX_MODE_AESGCM : HCRYPT_CTX_MODE_AESCTR; ctx->status = HCRYPT_CTX_S_INIT; - + ctx->use_gcm_153 = false; // Default initialization. ctx->msg_info = crypto->msg_info; if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) { @@ -184,7 +184,6 @@ int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto) ASSERT(HCRYPT_CTX_S_SARDY <= new_ctx->status); /* Keep same KEK, configuration, and salt */ -// memcpy(&new_ctx->aes_kek, &ctx->aes_kek, sizeof(new_ctx->aes_kek)); memcpy(&new_ctx->cfg, &ctx->cfg, sizeof(new_ctx->cfg)); new_ctx->salt_len = ctx->salt_len; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b802bdec9..908bf748c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2021,7 +2021,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) { uint32_t srtdata_out[SRTDATA_MAXSIZE]; size_t len_out = 0; - res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, CUDT::HS_VERSION_UDT4, + res = m_pCryptoControl->processSrtMsg_KMREQ(srtdata, len, CUDT::HS_VERSION_UDT4, m_uPeerSrtVersion, (srtdata_out), (len_out)); if (res == SRT_CMD_KMRSP) { @@ -2058,7 +2058,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) case SRT_CMD_KMRSP: { // KMRSP doesn't expect any following action - m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, CUDT::HS_VERSION_UDT4); + m_pCryptoControl->processSrtMsg_KMRSP(srtdata, len, m_uPeerSrtVersion); return true; // nothing to do } @@ -2647,7 +2647,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; } - int res = m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, HS_VERSION_SRT1, + int res = m_pCryptoControl->processSrtMsg_KMREQ(begin + 1, bytelen, HS_VERSION_SRT1, m_uPeerSrtVersion, (out_data), (*pw_len)); if (res != SRT_CMD_KMRSP) { @@ -2694,7 +2694,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, } else if (cmd == SRT_CMD_KMRSP) { - int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, HS_VERSION_SRT1); + int res = m_pCryptoControl->processSrtMsg_KMRSP(begin + 1, bytelen, m_uPeerSrtVersion); if (m_config.bEnforcedEnc && res == -1) { if (m_pCryptoControl->m_SndKmState == SRT_KM_S_BADSECRET) @@ -6023,13 +6023,15 @@ bool srt::CUDT::createCrypter(HandshakeSide side, bool bidirectional) // they have outdated values. m_pCryptoControl->setCryptoSecret(m_config.CryptoSecret); + const bool useGcm153 = m_uPeerSrtVersion <= SrtVersion(1, 5, 3); + if (bidirectional || m_config.bDataSender) { HLOGC(rslog.Debug, log << CONID() << "createCrypter: setting RCV/SND KeyLen=" << m_config.iSndCryptoKeyLen); m_pCryptoControl->setCryptoKeylen(m_config.iSndCryptoKeyLen); } - return m_pCryptoControl->init(side, m_config, bidirectional); + return m_pCryptoControl->init(side, m_config, bidirectional, useGcm153); } SRT_REJECT_REASON srt::CUDT::setupCC() @@ -7763,7 +7765,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - m_dCWndSize m_tdSendInterval = microseconds_from((int64_t)m_CongCtl->pktSndPeriod_us()); const double cgwindow = m_CongCtl->cgWindowSize(); - m_iCongestionWindow = cgwindow; + m_iCongestionWindow = (int) cgwindow; #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index fdd643f22..e7fe0be89 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -138,7 +138,7 @@ void srt::CCryptoControl::createFakeSndContext() int srt::CCryptoControl::processSrtMsg_KMREQ( const uint32_t* srtdata SRT_ATR_UNUSED, size_t bytelen SRT_ATR_UNUSED, - int hsv SRT_ATR_UNUSED, + int hsv SRT_ATR_UNUSED, unsigned srtv SRT_ATR_UNUSED, uint32_t pw_srtdata_out[], size_t& w_srtlen) { //Receiver @@ -172,6 +172,8 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO && kmdata[HCRYPT_MSG_KM_OFS_CIPHER] == HCRYPT_CIPHER_AES_GCM) || (m_iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM); + m_bUseGcm153 = srtv <= SrtVersion(1, 5, 3); + // What we have to do: // If encryption is on (we know that by having m_KmSecret nonempty), create // the crypto context (if bidirectional, create for both sending and receiving). @@ -331,6 +333,13 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX."); } +#ifdef SRT_ENABLE_ENCRYPTION + if (m_hRcvCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hRcvCrypto, m_bUseGcm153); + if (m_hSndCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hSndCrypto, m_bUseGcm153); +#endif + return SRT_CMD_KMRSP; HSv4_ErrorReport: @@ -359,7 +368,7 @@ int srt::CCryptoControl::processSrtMsg_KMREQ( return SRT_CMD_KMRSP; } -int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/) +int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, unsigned srtv) { /* All 32-bit msg fields (if present) swapped on reception * But HaiCrypt expect network order message @@ -371,9 +380,6 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len int retstatus = -1; - // Unused? - //bool bidirectional = hsv > CUDT::HS_VERSION_UDT4; - // Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE, // until the next KMREQ is received as a key regeneration. m_bErrorReported = false; @@ -459,6 +465,14 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len } HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry << "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry); + + m_bUseGcm153 = srtv <= SrtVersion(1, 5, 3); +#ifdef SRT_ENABLE_ENCRYPTION + if (m_hRcvCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hRcvCrypto, m_bUseGcm153); + if (m_hSndCrypto != NULL) + HaiCrypt_UpdateGcm153(m_hSndCrypto, m_bUseGcm153); +#endif } LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len)); @@ -593,6 +607,7 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id) , m_KmRefreshRatePkt(0) , m_KmPreAnnouncePkt(0) , m_iCryptoMode(CSrtConfig::CIPHER_MODE_AUTO) + , m_bUseGcm153(false) , m_bErrorReported(false) { m_KmSecret.len = 0; @@ -606,7 +621,7 @@ srt::CCryptoControl::CCryptoControl(SRTSOCKET id) m_hRcvCrypto = NULL; } -bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED) +bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED, bool bUseGcm153 SRT_ATR_UNUSED) { // NOTE: initiator creates m_hSndCrypto. When bidirectional, // it creates also m_hRcvCrypto with the same key length. @@ -620,6 +635,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b // Set UNSECURED state as default m_RcvKmState = SRT_KM_S_UNSECURED; m_iCryptoMode = cfg.iCryptoMode; + m_bUseGcm153 = bUseGcm153; #ifdef SRT_ENABLE_ENCRYPTION if (!cfg.bTSBPD && m_iCryptoMode == CSrtConfig::CIPHER_MODE_AUTO) @@ -807,7 +823,7 @@ srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNU { return ENCS_FAILED; } - else if ( rc > 0 ) + else if (rc > 0) { // XXX what happens if the encryption is said to be "succeeded", // but the length is 0? Shouldn't this be treated as unwanted? diff --git a/srtcore/crypto.h b/srtcore/crypto.h index 370d5529c..613ded8dd 100644 --- a/srtcore/crypto.h +++ b/srtcore/crypto.h @@ -68,6 +68,7 @@ class CCryptoControl int m_KmRefreshRatePkt; int m_KmPreAnnouncePkt; int m_iCryptoMode; + bool m_bUseGcm153; // Older AES-GCM version existed up to SRT v1.5.3. HaiCrypt_Secret m_KmSecret; //Key material shared secret // Sender @@ -127,15 +128,18 @@ class CCryptoControl // Needed for CUDT void updateKmState(int cmd, size_t srtlen); - // Detailed processing - int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv, + /// Process the KM request message. + /// @param srtv peer's SRT version. + int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv, unsigned srtv, uint32_t srtdata_out[], size_t&); - // This returns: - // 1 - the given payload is the same as the currently used key - // 0 - there's no key in agent or the payload is error message with agent NOSECRET. - // -1 - the payload is error message with other state or it doesn't match the key - int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int hsv); + /// Process the KM response message. + /// @param srtv peer's SRT version. + /// @returns + /// 1 - the given payload is the same as the currently used key + /// 0 - there's no key in agent or the payload is error message with agent NOSECRET. + /// -1 - the payload is error message with other state or it doesn't match the key + int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, unsigned srtv); void createFakeSndContext(); const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; } @@ -207,7 +211,7 @@ class CCryptoControl std::string CONID() const; std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen); - bool init(HandshakeSide, const CSrtConfig&, bool); + bool init(HandshakeSide, const CSrtConfig&, bool bidir, bool bUseGcm153); SRT_ATTR_EXCLUDES(m_mtxLock) void close(); From 8c1fa00aafa55d5e1b014e63f894f8b54de3161f Mon Sep 17 00:00:00 2001 From: yomnes0 Date: Fri, 19 Jul 2024 10:45:41 +0200 Subject: [PATCH 61/85] [tests] Fix warnings when compiling with g++ 11.4.0 on ubuntu 22.04 --- examples/recvlive.cpp | 2 +- examples/sendmsg.cpp | 10 +++++----- test/test_reuseaddr.cpp | 2 +- testing/srt-test-relay.cpp | 6 +++--- testing/testmedia.cpp | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index 81c2b1ef7..e14420a66 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -147,7 +147,7 @@ int main(int argc, char* argv[]) clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV); cout << "new connection: " << clienthost << ":" << clientservice << endl; - int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; + events = SRT_EPOLL_IN | SRT_EPOLL_ERR; if (SRT_ERROR == srt_epoll_add_usock(epid, fhandle, &events)) { cout << "srt_epoll_add_usock: " << srt_getlasterror_str() << endl; diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp index 53a0f66a5..af551849b 100644 --- a/examples/sendmsg.cpp +++ b/examples/sendmsg.cpp @@ -150,25 +150,25 @@ int main(int argc, char* argv[]) } else { - int lpos = 0; + int lpos2 = 0; int nparsed = 0; if (line[0] == '+') { - nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos2); if (nparsed != 2) { - cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos2 << ")\n"; status = SRT_ERROR; break; } } else { - nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos2); if (nparsed != 1) { - cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos2 << ")\n"; status = SRT_ERROR; break; } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index d00628e63..df4aec487 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -355,7 +355,7 @@ class ReuseAddr : public srt::Test sockaddr_any showacp = (sockaddr*)&scl; std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; - int epoll_in = SRT_EPOLL_IN; + epoll_in = SRT_EPOLL_IN; srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input char buffer[1316]; diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 45d657128..e7e5ae574 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -367,9 +367,9 @@ SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const strin Verb() << "SRT set up as input source and the first output target"; // Add SRT medium to output targets, and keep input medium empty. - unique_ptr m { new TargetMedium }; - m->Setup(m_srt_relay.get()); - m_output_media.push_back(move(m)); + unique_ptr med { new TargetMedium }; + med->Setup(m_srt_relay.get()); + m_output_media.push_back(move(med)); } else { diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..d82212bad 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1130,15 +1130,15 @@ void SrtCommon::OpenGroupClient() // spread the setting on all sockets. ConfigurePost(m_sock); - for (size_t i = 0; i < targets.size(); ++i) + for (size_t j = 0; j < targets.size(); ++j) { // As m_group_nodes is simply transformed into 'targets', // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[j].id != -1 && targets[j].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[j].socket = targets[j].id; } } @@ -1159,9 +1159,9 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t j = 0; j < m_group_nodes.size(); ++j) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[j].socket; if (insock == -1) { Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; From fbc3e04481e775a77124af2c964038f08a48b22f Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Thu, 1 Aug 2024 15:02:20 +0200 Subject: [PATCH 62/85] [build] Removed VC 2013. Added VC 2022 (#2988). --- .appveyor.yml | 10 +- scripts/build-windows.ps1 | 473 +++++++++++++++++++------------------- 2 files changed, 238 insertions(+), 245 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 96b785d93..1254c9c16 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,9 +3,9 @@ configuration: - Debug image: + - Visual Studio 2022 - Visual Studio 2019 - Visual Studio 2015 - - Visual Studio 2013 platform: - x64 @@ -14,12 +14,8 @@ platform: build_script: - ps: $VSIMG = $Env:APPVEYOR_BUILD_WORKER_IMAGE; $CNFG = $Env:CONFIGURATION # use a few differing arguments depending on VS version to exercise different options during builds - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON } - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON } - - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON} - - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS OFF } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -CXX11 OFF -BUILD_APPS ON } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Debug") { Exit-AppveyorBuild } # just skip 2013 debug build for speed + - ps: if ($CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON} + - ps: if ($CNFG -eq "Debug") { if ($VSIMG -match '2015') { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS OFF } else {.\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON }} test_script: - ps: if ( $Env:RUN_UNIT_TESTS ) { cd ./_build; ctest -E "TestIPv6.v6_calls_v4" --extra-verbose -C $Env:CONFIGURATION; cd ../ } diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index 4db3fe627..66586158e 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -1,238 +1,235 @@ -################################################################################ -# Windows SRT Build Script -#============================ -# Usable on a Windows PC with Powershell and Visual studio, -# or called by CI systems like AppVeyor -# -# 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, install OpenSSL or set VCKPG flag to build -################################################################################ - -param ( - [Parameter()][String]$VS_VERSION = "2019", - [Parameter()][String]$CONFIGURATION = "Release", - [Parameter()][String]$DEVENV_PLATFORM = "x64", - [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", - [Parameter()][String]$STATIC_LINK_SSL = "OFF", - [Parameter()][String]$CXX11 = "ON", - [Parameter()][String]$BUILD_APPS = "ON", - [Parameter()][String]$UNIT_TESTS = "OFF", - [Parameter()][String]$BUILD_DIR = "_build", - [Parameter()][String]$VCPKG_OPENSSL = "OFF", - [Parameter()][String]$BONDING = "OFF" -) - -# cmake can be optionally installed (useful when running interactively on a developer station). -# The URL for automatic download is defined later in the script, but it should be possible to just vary the -# specific version set below and the URL should be stable enough to still work - you have been warned. -$cmakeVersion = "3.23.2" - -# make all errors trigger a script stop, rather than just carry on -$ErrorActionPreference = "Stop" - -$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve - -# if running within AppVeyor, use environment variables to set params instead of passed-in values -if ( $Env:APPVEYOR ) { - if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2013' ) { $VS_VERSION='2013' } - - #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package - if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true } - - #if unit tests are on, set flag to actually execute ctest step - if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true } - - $CONFIGURATION = $Env:CONFIGURATION - - #appveyor has many openssl installations - place the latest one in the default location unless VS2013 - if( $VS_VERSION -ne '2013' ) { - Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null - } -} - -# persist VS_VERSION so it can be used in an artifact name later -$Env:VS_VERSION = $VS_VERSION - -# select the appropriate cmake generator string given the environment -if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; } -if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; } -if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; } -if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013'; $MSBUILDVER = "12.0"; } -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_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 -Push-Location $buildDir - -# check cmake is installed -if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { - $installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]" - - if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) { - # download cmake and run MSI for user - $client = New-Object System.Net.WebClient - $tempDownloadFile = New-TemporaryFile - - $cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi" - $cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi" - Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)" - Write-Output "Note: select the option to add cmake to path for this script to operate" - $client.DownloadFile("$cmakeUrl", "$cmakeMsiFile") - 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" - throw - } - else{ - Write-Output "Quitting because cmake is required" - throw - } -} - -# get pthreads from nuget if CXX11 is not enabled -if ( $CXX11 -eq "OFF" ) { - # get pthreads (this is legacy, and is only available 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" - throw - } - if ( $DEVENV_PLATFORM -eq 'Win32' ) { - nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages - } - else { - nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages - } -} - -# 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 - Write-Output "Static linking to OpenSSL requested, will force encryption feature ON" - $ENABLE_ENCRYPTION = "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 " + - "-DENABLE_BONDING=$BONDING " + - "-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`"" -} - -# fire cmake to build project files -$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags" -Write-Output $execVar - -# Reset reaction to Continue for cmake as it sometimes tends to print -# things on stderr, which is understood by PowerShell as error. The -# exit code from cmake will be checked anyway. -$ErrorActionPreference = "Continue" -Invoke-Expression "& $execVar" - -# check build ran OK, exit if cmake failed -if( $LASTEXITCODE -ne 0 ) { - Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" - throw -} - -$ErrorActionPreference = "Stop" - -# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL -. $PSScriptRoot/set-version-metadata.ps1 - -# look for msbuild -$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue -if ( $null -eq $msBuildPath ) { - # no mbsuild in the path, so try to locate with 'vswhere' - $vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue - if ( $null -eq $vsWherePath ) { - # no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2) - $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" - throw - } - } - $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 -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM - -# return to the directory previously occupied before running the script -Pop-Location - -# if msbuild returned non-zero, throw to cause failure in CI -if( $LASTEXITCODE -ne 0 ) { - throw -} +################################################################################ +# Windows SRT Build Script +#============================ +# Usable on a Windows PC with Powershell and Visual studio, +# or called by CI systems like AppVeyor +# +# 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, install OpenSSL or set VCKPG flag to build +################################################################################ + +param ( + [Parameter()][String]$VS_VERSION = "2022", + [Parameter()][String]$CONFIGURATION = "Release", + [Parameter()][String]$DEVENV_PLATFORM = "x64", + [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", + [Parameter()][String]$STATIC_LINK_SSL = "OFF", + [Parameter()][String]$CXX11 = "ON", + [Parameter()][String]$BUILD_APPS = "ON", + [Parameter()][String]$UNIT_TESTS = "OFF", + [Parameter()][String]$BUILD_DIR = "_build", + [Parameter()][String]$VCPKG_OPENSSL = "OFF", + [Parameter()][String]$BONDING = "OFF" +) + +# cmake can be optionally installed (useful when running interactively on a developer station). +# The URL for automatic download is defined later in the script, but it should be possible to just vary the +# specific version set below and the URL should be stable enough to still work - you have been warned. +$cmakeVersion = "3.23.2" + +# make all errors trigger a script stop, rather than just carry on +$ErrorActionPreference = "Stop" + +$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve + +# if running within AppVeyor, use environment variables to set params instead of passed-in values +if ( $Env:APPVEYOR ) { + if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2022' ) { $VS_VERSION='2022' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' } + + #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package + if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true } + + #if unit tests are on, set flag to actually execute ctest step + if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true } + + $CONFIGURATION = $Env:CONFIGURATION + + #appveyor has many openssl installations - place the latest one in the default location unless VS2013 + Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null +} + +# persist VS_VERSION so it can be used in an artifact name later +$Env:VS_VERSION = $VS_VERSION + +# select the appropriate cmake generator string given the environment +if ( $VS_VERSION -eq '2022' ) { $CMAKE_GENERATOR = 'Visual Studio 17 2022'; $MSBUILDVER = "17.10"; } +if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; } +if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; } +if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; } + +# clear any previous build and create & enter the build directory +$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 +Push-Location $buildDir + +# check cmake is installed +if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) { + $installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]" + + if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) { + # download cmake and run MSI for user + $client = New-Object System.Net.WebClient + $tempDownloadFile = New-TemporaryFile + + $cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi" + $cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi" + Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)" + Write-Output "Note: select the option to add cmake to path for this script to operate" + $client.DownloadFile("$cmakeUrl", "$cmakeMsiFile") + 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" + throw + } + else{ + Write-Output "Quitting because cmake is required" + throw + } +} + +# get pthreads from nuget if CXX11 is not enabled +if ( $CXX11 -eq "OFF" ) { + # get pthreads (this is legacy, and is only available 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" + throw + } + if ( $DEVENV_PLATFORM -eq 'Win32' ) { + nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages + } + else { + nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages + } +} + +# 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 + Write-Output "Static linking to OpenSSL requested, will force encryption feature ON" + $ENABLE_ENCRYPTION = "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 " + + "-DENABLE_BONDING=$BONDING " + + "-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 -ge '2019' ) { + $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" +} + +# fire cmake to build project files +$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags" +Write-Output $execVar + +# Reset reaction to Continue for cmake as it sometimes tends to print +# things on stderr, which is understood by PowerShell as error. The +# exit code from cmake will be checked anyway. +$ErrorActionPreference = "Continue" +Invoke-Expression "& $execVar" + +# check build ran OK, exit if cmake failed +if( $LASTEXITCODE -ne 0 ) { + Write-Output "Non-zero exit code from cmake: $LASTEXITCODE" + throw +} + +$ErrorActionPreference = "Stop" + +# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL +. $PSScriptRoot/set-version-metadata.ps1 + +# look for msbuild +$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue +if ( $null -eq $msBuildPath ) { + # no mbsuild in the path, so try to locate with 'vswhere' + $vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue + if ( $null -eq $vsWherePath ) { + # no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2) + $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" + throw + } + } + $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 -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM + +# return to the directory previously occupied before running the script +Pop-Location + +# if msbuild returned non-zero, throw to cause failure in CI +if( $LASTEXITCODE -ne 0 ) { + throw +} From ffdebd334d411d48a075412bdd90a277b279a91c Mon Sep 17 00:00:00 2001 From: yomnes0 <127947185+yomnes0@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:51:17 +0200 Subject: [PATCH 63/85] [core] Added a shared mutex class (#2981). Co-authored-by: Maxim Sharabayko --- srtcore/sync.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++ srtcore/sync.h | 37 ++++++++++++++++++ test/test_sync.cpp | 85 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index a7cebb909..bfe153657 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -357,3 +357,98 @@ int srt::sync::genRandomInt(int minVal, int maxVal) #endif // HAVE_CXX11 } + +//////////////////////////////////////////////////////////////////////////////// +// +// Shared Mutex +// +//////////////////////////////////////////////////////////////////////////////// + +srt::sync::SharedMutex::SharedMutex() + : m_LockWriteCond() + , m_LockReadCond() + , m_Mutex() + , m_iCountRead(0) + , m_bWriterLocked(false) +{ + setupCond(m_LockReadCond, "SharedMutex::m_pLockReadCond"); + setupCond(m_LockWriteCond, "SharedMutex::m_pLockWriteCond"); + setupMutex(m_Mutex, "SharedMutex::m_pMutex"); +} + +srt::sync::SharedMutex::~SharedMutex() +{ + releaseMutex(m_Mutex); + releaseCond(m_LockWriteCond); + releaseCond(m_LockReadCond); +} + +void srt::sync::SharedMutex::lock() +{ + UniqueLock l1(m_Mutex); + while (m_bWriterLocked) + m_LockWriteCond.wait(l1); + + m_bWriterLocked = true; + + while (m_iCountRead) + m_LockReadCond.wait(l1); +} + +bool srt::sync::SharedMutex::try_lock() +{ + UniqueLock l1(m_Mutex); + if (m_bWriterLocked || m_iCountRead > 0) + return false; + + m_bWriterLocked = true; + return true; +} + +void srt::sync::SharedMutex::unlock() +{ + ScopedLock lk(m_Mutex); + m_bWriterLocked = false; + + m_LockWriteCond.notify_all(); +} + +void srt::sync::SharedMutex::lock_shared() +{ + UniqueLock lk(m_Mutex); + while (m_bWriterLocked) + m_LockWriteCond.wait(lk); + + m_iCountRead++; +} + +bool srt::sync::SharedMutex::try_lock_shared() +{ + UniqueLock lk(m_Mutex); + if (m_bWriterLocked) + return false; + + m_iCountRead++; + return true; +} + +void srt::sync::SharedMutex::unlock_shared() +{ + ScopedLock lk(m_Mutex); + + m_iCountRead--; + + SRT_ASSERT(m_iCountRead >= 0); + if (m_iCountRead < 0) + m_iCountRead = 0; + + if (m_bWriterLocked && m_iCountRead == 0) + m_LockReadCond.notify_one(); + +} + +int srt::sync::SharedMutex::getReaderCount() const +{ + ScopedLock lk(m_Mutex); + return m_iCountRead; +} \ No newline at end of file diff --git a/srtcore/sync.h b/srtcore/sync.h index fb6d56432..8fee25831 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -943,6 +943,43 @@ CUDTException& GetThreadLocalError(); /// @param[in] maxVal maximum allowed value of the resulting random number. int genRandomInt(int minVal, int maxVal); + +/// Implementation of a read-write mutex. +/// This allows multiple readers at a time, or a single writer. +/// TODO: The class can be improved if needed to give writer a preference +/// by adding additional m_iWritersWaiting member variable (counter). +/// TODO: The m_iCountRead could be made atomic to make unlok_shared() faster and lock-free. +class SharedMutex +{ +public: + SharedMutex(); + ~SharedMutex(); + +private: + Condition m_LockWriteCond; + Condition m_LockReadCond; + + mutable Mutex m_Mutex; + + int m_iCountRead; + bool m_bWriterLocked; + + /// Acquire the lock for writting purposes. Only one thread can acquire this lock at a time + /// Once it is locked, no reader can acquire it +public: + void lock(); + bool try_lock(); + void unlock(); + + /// Acquire the lock if no writter already has it. For read purpose only + /// Several readers can lock this at the same time. + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); + + int getReaderCount() const; +}; + } // namespace sync } // namespace srt diff --git a/test/test_sync.cpp b/test/test_sync.cpp index e0454a581..9e2064cc1 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -609,6 +609,91 @@ TEST(SyncThread, Joinable) EXPECT_FALSE(foo.joinable()); } +/*****************************************************************************/ +/* + * SharedMutex + */ + /*****************************************************************************/ +TEST(SharedMutex, LockWriteRead) +{ + SharedMutex mut; + + mut.lock(); + EXPECT_FALSE(mut.try_lock_shared()); + +} + +TEST(SharedMutex, LockReadWrite) +{ + SharedMutex mut; + + mut.lock_shared(); + EXPECT_FALSE(mut.try_lock()); + +} + +TEST(SharedMutex, LockReadTwice) +{ + SharedMutex mut; + + mut.lock_shared(); + mut.lock_shared(); + EXPECT_TRUE(mut.try_lock_shared()); +} + +TEST(SharedMutex, LockWriteTwice) +{ + SharedMutex mut; + + mut.lock(); + EXPECT_FALSE(mut.try_lock()); +} + +TEST(SharedMutex, LockUnlockWrite) +{ + SharedMutex mut; + mut.lock(); + EXPECT_FALSE(mut.try_lock()); + mut.unlock(); + EXPECT_TRUE(mut.try_lock()); +} + +TEST(SharedMutex, LockUnlockRead) +{ + SharedMutex mut; + + mut.lock_shared(); + EXPECT_FALSE(mut.try_lock()); + + mut.unlock_shared(); + EXPECT_TRUE(mut.try_lock()); +} + +TEST(SharedMutex, LockedReadCount) +{ + SharedMutex mut; + int count = 0; + + mut.lock_shared(); + count++; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.lock_shared(); + count++; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.unlock_shared(); + count--; + ASSERT_EQ(mut.getReaderCount(), count); + + mut.unlock_shared(); + count--; + ASSERT_EQ(mut.getReaderCount(), count); + + EXPECT_TRUE(mut.try_lock()); +} + + /*****************************************************************************/ /* * FormatTime From 968c9f9613558be99e4e58e9908a6feeade479f2 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 2 Aug 2024 14:10:20 +0200 Subject: [PATCH 64/85] [core] Set ACK position based on RCV buffer first nonread seqno (#2931). --- srtcore/buffer_rcv.cpp | 6 ++++++ srtcore/buffer_rcv.h | 5 +++++ srtcore/core.cpp | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 2ec42487d..363bd7e9c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -760,6 +760,12 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no return unreadableInfo; } +int32_t CRcvBuffer::getFirstNonreadSeqNo() const +{ + const int offset = offPos(m_iStartPos, m_iFirstNonreadPos); + return CSeqNo::incseq(m_iStartSeqNo, offset); +} + void CRcvBuffer::countBytes(int pkts, int bytes) { ScopedLock lock(m_BytesCountLock); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index f783ac2a2..c5fca428b 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -187,6 +187,11 @@ class CRcvBuffer PacketInfo getFirstReadablePacketInfo(time_point time_now) const; + /// @brief Get the sequence number of the first packet that can't be read + /// (either because it is missing, or because it is a part of a bigger message + /// that is not fully available yet). + int32_t getFirstNonreadSeqNo() const; + /// Get information on packets available to be read. /// @returns a pair of sequence numbers (first available; first unavailable). /// diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 908bf748c..9715a209e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8023,6 +8023,27 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp // [[using locked(m_RcvBufferLock)]] bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) { + if (m_config.bTSBPD || !m_config.bMessageAPI) + { + // The getFirstNonreadSeqNo() function retuens the sequence number of the first packet + // that cannot be read. In cases when a message can consist of several data packets, + // an existing packet of partially available message also cannot be read. + // If TSBPD mode is enabled, a message must consist of a single data packet only. + w_seq = m_pRcvBuffer->getFirstNonreadSeqNo(); + + const int32_t iNextSeqNo = CSeqNo::incseq(m_iRcvCurrSeqNo); + SRT_ASSERT(CSeqNo::seqcmp(w_seq, iNextSeqNo) <= 0); + w_log_reason = iNextSeqNo != w_seq ? "first lost" : "expected next"; + + if (CSeqNo::seqcmp(w_seq, iNextSeqNo) > 0) + { + LOGC(xtlog.Error, log << "IPE: NONCONT-SEQUENCE: RCV buffer first non-read %" << w_seq << ", RCV latest seqno %" << m_iRcvCurrSeqNo); + w_seq = iNextSeqNo; + } + + return true; + } + { ScopedLock losslock (m_RcvLossLock); const int32_t seq = m_pRcvLossList->getFirstLostSeq(); @@ -8070,6 +8091,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // since the last full ACK. It should unblock the sender to proceed further. const bool bNeedFullAck = (m_bBufferWasFull && getAvailRcvBufferSizeNoLock() > 0); int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) return nbsent; From 77eed81b25ce0ecfe283805684b35a0571a9fe4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 2 Aug 2024 15:24:35 +0200 Subject: [PATCH 65/85] [core] Applied clang-format on cache.h and cache.cpp. --- srtcore/cache.cpp | 93 ++++++------ srtcore/cache.h | 351 +++++++++++++++++++++++----------------------- 2 files changed, 219 insertions(+), 225 deletions(-) diff --git a/srtcore/cache.cpp b/srtcore/cache.cpp index 4cda9a70f..2c6f624c4 100644 --- a/srtcore/cache.cpp +++ b/srtcore/cache.cpp @@ -38,7 +38,6 @@ written by Yunhong Gu, last updated 05/05/2009 *****************************************************************************/ - #include "platform_sys.h" #include @@ -49,70 +48,70 @@ using namespace std; srt::CInfoBlock& srt::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_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; - - return *this; + std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP); + 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; + + return *this; } bool srt::CInfoBlock::operator==(const CInfoBlock& obj) const { - if (m_iIPversion != obj.m_iIPversion) - return false; + if (m_iIPversion != obj.m_iIPversion) + return false; - else if (m_iIPversion == AF_INET) - return (m_piIP[0] == obj.m_piIP[0]); + else if (m_iIPversion == AF_INET) + return (m_piIP[0] == obj.m_piIP[0]); - for (int i = 0; i < 4; ++ i) - { - if (m_piIP[i] != obj.m_piIP[i]) - return false; - } + for (int i = 0; i < 4; ++i) + { + if (m_piIP[i] != obj.m_piIP[i]) + return false; + } - return true; + return true; } srt::CInfoBlock* srt::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_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; - - return obj; + 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_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; + + return obj; } int srt::CInfoBlock::getKey() { - if (m_iIPversion == AF_INET) - return m_piIP[0]; + if (m_iIPversion == AF_INET) + return m_piIP[0]; - return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; + return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3]; } void srt::CInfoBlock::convert(const sockaddr_any& addr, uint32_t aw_ip[4]) { - if (addr.family() == AF_INET) - { - aw_ip[0] = addr.sin.sin_addr.s_addr; - aw_ip[1] = aw_ip[2] = aw_ip[3] = 0; - } - else - { - memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr); - } + if (addr.family() == AF_INET) + { + aw_ip[0] = addr.sin.sin_addr.s_addr; + aw_ip[1] = aw_ip[2] = aw_ip[3] = 0; + } + else + { + memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr); + } } diff --git a/srtcore/cache.h b/srtcore/cache.h index d5a037633..d7f586c20 100644 --- a/srtcore/cache.h +++ b/srtcore/cache.h @@ -54,220 +54,215 @@ namespace srt class CCacheItem { public: - virtual ~CCacheItem() {} + virtual ~CCacheItem() {} public: - virtual CCacheItem& operator=(const CCacheItem&) = 0; + virtual CCacheItem& operator=(const CCacheItem&) = 0; - // The "==" operator SHOULD only compare key values. - virtual bool operator==(const CCacheItem&) = 0; + // The "==" operator SHOULD only compare key values. + virtual bool operator==(const CCacheItem&) = 0; - /// get a deep copy clone of the current item - /// @return Pointer to the new item, or NULL if failed. + /// get a deep copy clone of the current item + /// @return Pointer to the new item, or NULL if failed. - virtual CCacheItem* clone() = 0; + virtual CCacheItem* clone() = 0; - /// get a random key value between 0 and MAX_INT to be used for the hash in cache - /// @return A random hash key. + /// get a random key value between 0 and MAX_INT to be used for the hash in cache + /// @return A random hash key. - virtual int getKey() = 0; + virtual int getKey() = 0; - // If there is any shared resources between the cache item and its clone, - // the shared resource should be released by this function. - virtual void release() {} + // If there is any shared resources between the cache item and its clone, + // the shared resource should be released by this function. + virtual void release() {} }; -template class CCache +template +class CCache { public: - CCache(int size = 1024): - m_iMaxSize(size), - m_iHashSize(size * 3), - m_iCurrSize(0) - { - m_vHashPtr.resize(m_iHashSize); - // Exception: -> CUDTUnited ctor - srt::sync::setupMutex(m_Lock, "Cache"); - } - - ~CCache() - { - clear(); - } + CCache(int size = 1024) + : m_iMaxSize(size) + , m_iHashSize(size * 3) + , m_iCurrSize(0) + { + m_vHashPtr.resize(m_iHashSize); + // Exception: -> CUDTUnited ctor + srt::sync::setupMutex(m_Lock, "Cache"); + } + + ~CCache() { clear(); } public: - /// find the matching item in the cache. - /// @param [in,out] data storage for the retrieved item; initially it must carry the key information - /// @return 0 if found a match, otherwise -1. - - int lookup(T* data) - { - srt::sync::ScopedLock cacheguard(m_Lock); - - int key = data->getKey(); - if (key < 0) - return -1; - if (key >= m_iMaxSize) - key %= m_iHashSize; - - const ItemPtrList& item_list = m_vHashPtr[key]; - for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i) - { - if (*data == ***i) - { - // copy the cached info - *data = ***i; - return 0; - } - } - - return -1; - } - - /// update an item in the cache, or insert one if it doesn't exist; oldest item may be removed - /// @param [in] data the new item to updated/inserted to the cache - /// @return 0 if success, otherwise -1. - - int update(T* data) - { - srt::sync::ScopedLock cacheguard(m_Lock); - - int key = data->getKey(); - if (key < 0) - return -1; - if (key >= m_iMaxSize) - key %= m_iHashSize; - - T* curr = NULL; - - ItemPtrList& item_list = m_vHashPtr[key]; - for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i) - { - if (*data == ***i) - { - // update the existing entry with the new value - ***i = *data; - curr = **i; - - // remove the current entry - m_StorageList.erase(*i); - item_list.erase(i); - - // re-insert to the front - m_StorageList.push_front(curr); - item_list.push_front(m_StorageList.begin()); - - return 0; - } - } - - // create new entry and insert to front - curr = data->clone(); - m_StorageList.push_front(curr); - item_list.push_front(m_StorageList.begin()); - - ++ m_iCurrSize; - if (m_iCurrSize >= m_iMaxSize) - { - // Cache overflow, remove oldest entry. - T* last_data = m_StorageList.back(); - int last_key = last_data->getKey() % m_iHashSize; - - ItemPtrList& last_item_list = m_vHashPtr[last_key]; - for (typename ItemPtrList::iterator i = last_item_list.begin(); i != last_item_list.end(); ++ i) - { - if (*last_data == ***i) + /// find the matching item in the cache. + /// @param [in,out] data storage for the retrieved item; initially it must carry the key information + /// @return 0 if found a match, otherwise -1. + + int lookup(T* data) + { + srt::sync::ScopedLock cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + const ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++i) + { + if (*data == ***i) { - last_item_list.erase(i); - break; + // copy the cached info + *data = ***i; + return 0; } - } + } - last_data->release(); - delete last_data; - m_StorageList.pop_back(); - -- m_iCurrSize; - } + return -1; + } - return 0; - } + /// update an item in the cache, or insert one if it doesn't exist; oldest item may be removed + /// @param [in] data the new item to updated/inserted to the cache + /// @return 0 if success, otherwise -1. -private: + int update(T* data) + { + srt::sync::ScopedLock cacheguard(m_Lock); + + int key = data->getKey(); + if (key < 0) + return -1; + if (key >= m_iMaxSize) + key %= m_iHashSize; + + T* curr = NULL; + + ItemPtrList& item_list = m_vHashPtr[key]; + for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++i) + { + if (*data == ***i) + { + // update the existing entry with the new value + ***i = *data; + curr = **i; + + // remove the current entry + m_StorageList.erase(*i); + item_list.erase(i); + + // re-insert to the front + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + return 0; + } + } + + // create new entry and insert to front + curr = data->clone(); + m_StorageList.push_front(curr); + item_list.push_front(m_StorageList.begin()); + + ++m_iCurrSize; + if (m_iCurrSize >= m_iMaxSize) + { + // Cache overflow, remove oldest entry. + T* last_data = m_StorageList.back(); + int last_key = last_data->getKey() % m_iHashSize; + + ItemPtrList& last_item_list = m_vHashPtr[last_key]; + for (typename ItemPtrList::iterator i = last_item_list.begin(); i != last_item_list.end(); ++i) + { + if (*last_data == ***i) + { + last_item_list.erase(i); + break; + } + } + + last_data->release(); + delete last_data; + m_StorageList.pop_back(); + --m_iCurrSize; + } - /// Specify the cache size (i.e., max number of items). - /// Private or else must be protected by a lock. - /// @param [in] size max cache size. - void setSizeLimit(int size) - { - m_iMaxSize = size; - m_iHashSize = size * 3; - m_vHashPtr.resize(m_iHashSize); - } - - /// Clear all entries in the cache, restore to initialization state. - /// Private or else must be protected by a lock. - void clear() - { - for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i) - { - (*i)->release(); - delete *i; - } - m_StorageList.clear(); - for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i) - i->clear(); - m_iCurrSize = 0; - } + return 0; + } private: - std::list m_StorageList; - typedef typename std::list::iterator ItemPtr; - typedef std::list ItemPtrList; - std::vector m_vHashPtr; + /// Specify the cache size (i.e., max number of items). + /// Private or else must be protected by a lock. + /// @param [in] size max cache size. + void setSizeLimit(int size) + { + m_iMaxSize = size; + m_iHashSize = size * 3; + m_vHashPtr.resize(m_iHashSize); + } + + /// Clear all entries in the cache, restore to initialization state. + /// Private or else must be protected by a lock. + void clear() + { + for (typename std::list::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++i) + { + (*i)->release(); + delete *i; + } + m_StorageList.clear(); + for (typename std::vector::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++i) + i->clear(); + m_iCurrSize = 0; + } - int m_iMaxSize; - int m_iHashSize; - int m_iCurrSize; +private: + std::list m_StorageList; + typedef typename std::list::iterator ItemPtr; + typedef std::list ItemPtrList; + std::vector m_vHashPtr; + + int m_iMaxSize; + int m_iHashSize; + int m_iCurrSize; - srt::sync::Mutex m_Lock; + srt::sync::Mutex m_Lock; private: - CCache(const CCache&); - CCache& operator=(const CCache&); + CCache(const CCache&); + CCache& operator=(const 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_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). + 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 - CInfoBlock& copyFrom(const CInfoBlock& obj); - CInfoBlock(const CInfoBlock& src) { copyFrom(src); } - CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); } - bool operator==(const CInfoBlock& obj) const; - CInfoBlock* clone(); - int getKey(); - void release() {} + CInfoBlock() {} // NOTE: leaves uninitialized + CInfoBlock& copyFrom(const CInfoBlock& obj); + CInfoBlock(const CInfoBlock& src) { copyFrom(src); } + CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); } + bool operator==(const CInfoBlock& obj) const; + CInfoBlock* clone(); + int getKey(); + void release() {} public: + /// convert sockaddr structure to an integer array + /// @param [in] addr network address + /// @param [in] ver IP version + /// @param [out] ip the result machine readable IP address in integer array - /// convert sockaddr structure to an integer array - /// @param [in] addr network address - /// @param [in] ver IP version - /// @param [out] ip the result machine readable IP address in integer array - - static void convert(const sockaddr_any& addr, uint32_t ip[4]); + static void convert(const sockaddr_any& addr, uint32_t ip[4]); }; } // namespace srt From cd9615a3af132d30cf76ec8acf176a46d2018adc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 5 Aug 2024 14:58:50 +0200 Subject: [PATCH 66/85] [core] Added SharedLock to srt::sync. Added srt::sync::ExclusiveLock. Added srt::sync::CSharedObjectPtr. --- srtcore/sync.h | 163 +++++++++++++++++++++++++++++++---------- srtcore/sync_posix.cpp | 12 --- 2 files changed, 123 insertions(+), 52 deletions(-) diff --git a/srtcore/sync.h b/srtcore/sync.h index 8fee25831..e8b7444bd 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -343,15 +343,19 @@ class SRT_ATTR_CAPABILITY("mutex") Mutex pthread_mutex_t m_mutex; }; -/// A pthread version of std::chrono::scoped_lock (or lock_guard for C++11) +/// A pthread version of std::scoped_lock (or lock_guard for C++11). class SRT_ATTR_SCOPED_CAPABILITY ScopedLock { public: SRT_ATTR_ACQUIRE(m) - explicit ScopedLock(Mutex& m); + explicit ScopedLock(Mutex& m) + : m_mutex(m) + { + m_mutex.lock(); + } SRT_ATTR_RELEASE() - ~ScopedLock(); + ~ScopedLock() { m_mutex.unlock(); } private: Mutex& m_mutex; @@ -481,6 +485,122 @@ class Condition inline void setupCond(Condition& cv, const char*) { cv.init(); } inline void releaseCond(Condition& cv) { cv.destroy(); } +/////////////////////////////////////////////////////////////////////////////// +// +// Shared Mutex section +// +/////////////////////////////////////////////////////////////////////////////// + +/// Implementation of a read-write mutex. +/// This allows multiple readers at a time, or a single writer. +/// TODO: The class can be improved if needed to give writer a preference +/// by adding additional m_iWritersWaiting member variable (counter). +/// TODO: The m_iCountRead could be made atomic to make unlok_shared() faster and lock-free. +class SharedMutex +{ +public: + SharedMutex(); + ~SharedMutex(); + +public: + /// Acquire the lock for writting purposes. Only one thread can acquire this lock at a time + /// Once it is locked, no reader can acquire it + void lock(); + bool try_lock(); + void unlock(); + + /// Acquire the lock if no writter already has it. For read purpose only + /// Several readers can lock this at the same time. + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); + + int getReaderCount() const; + +protected: + Condition m_LockWriteCond; + Condition m_LockReadCond; + + mutable Mutex m_Mutex; + + int m_iCountRead; + bool m_bWriterLocked; +}; + +/// A version of std::scoped_lock (or lock_guard for C++11). +/// We could have used the srt::sync::ScopedLock making it a template-based class. +/// But in that case all usages would have to be specificed like ScopedLock in C++03. +class SRT_ATTR_SCOPED_CAPABILITY ExclusiveLock +{ +public: + SRT_ATTR_ACQUIRE(m) + explicit ExclusiveLock(SharedMutex& m) + : m_mutex(m) + { + m_mutex.lock(); + } + + SRT_ATTR_RELEASE() + ~ExclusiveLock() { m_mutex.unlock(); } + +private: + SharedMutex& m_mutex; +}; + +/// A reduced implementation of the std::shared_lock functionality (available in C++14). +class SRT_ATTR_SCOPED_CAPABILITY SharedLock +{ +public: + SRT_ATTR_ACQUIRE_SHARED(m) + explicit SharedLock(SharedMutex& m) + : m_mtx(m) + { + m_mtx.lock_shared(); + } + + SRT_ATTR_RELEASE_SHARED(m_mtx) + ~SharedLock() { m_mtx.unlock_shared(); } + +private: + SharedMutex& m_mtx; +}; + +/// A class template for a shared object. It is a wrapper around a pointer to an object +/// and a shared mutex. It allows multiple readers to access the object at the same time, +/// but only one writer can access the object at a time. +template +class CSharedObjectPtr : public SharedMutex +{ +public: + CSharedObjectPtr() + : m_pObj(NULL) + { + } + + bool set(T* pObj) + { + ExclusiveLock lock(*this); + if (m_pObj) + return false; + m_pObj = pObj; + return true; + } + + bool clearIf(const T* pObj) + { + ExclusiveLock lock(*this); + if (m_pObj != pObj) + return false; + m_pObj = NULL; + return true; + } + + T* getPtrNoLock() const { return m_pObj; } + +private: + T* m_pObj; +}; + /////////////////////////////////////////////////////////////////////////////// // // Event (CV) section @@ -943,43 +1063,6 @@ CUDTException& GetThreadLocalError(); /// @param[in] maxVal maximum allowed value of the resulting random number. int genRandomInt(int minVal, int maxVal); - -/// Implementation of a read-write mutex. -/// This allows multiple readers at a time, or a single writer. -/// TODO: The class can be improved if needed to give writer a preference -/// by adding additional m_iWritersWaiting member variable (counter). -/// TODO: The m_iCountRead could be made atomic to make unlok_shared() faster and lock-free. -class SharedMutex -{ -public: - SharedMutex(); - ~SharedMutex(); - -private: - Condition m_LockWriteCond; - Condition m_LockReadCond; - - mutable Mutex m_Mutex; - - int m_iCountRead; - bool m_bWriterLocked; - - /// Acquire the lock for writting purposes. Only one thread can acquire this lock at a time - /// Once it is locked, no reader can acquire it -public: - void lock(); - bool try_lock(); - void unlock(); - - /// Acquire the lock if no writter already has it. For read purpose only - /// Several readers can lock this at the same time. - void lock_shared(); - bool try_lock_shared(); - void unlock_shared(); - - int getReaderCount() const; -}; - } // namespace sync } // namespace srt diff --git a/srtcore/sync_posix.cpp b/srtcore/sync_posix.cpp index 8cb475ea7..8d7561a19 100644 --- a/srtcore/sync_posix.cpp +++ b/srtcore/sync_posix.cpp @@ -230,18 +230,6 @@ bool srt::sync::Mutex::try_lock() return (pthread_mutex_trylock(&m_mutex) == 0); } -srt::sync::ScopedLock::ScopedLock(Mutex& m) - : m_mutex(m) -{ - m_mutex.lock(); -} - -srt::sync::ScopedLock::~ScopedLock() -{ - m_mutex.unlock(); -} - - srt::sync::UniqueLock::UniqueLock(Mutex& m) : m_Mutex(m) { From 19194793f74e8424ac12c540c0e14f80f9b18999 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 5 Aug 2024 14:59:17 +0200 Subject: [PATCH 67/85] [core] Changed CUDT::m_pListener to CSharedObjectPtr. --- srtcore/queue.cpp | 22 ++++++++-------------- srtcore/queue.h | 5 ++--- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 98999a81f..7adc9aa47 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1129,8 +1129,6 @@ srt::CRcvQueue::CRcvQueue() , m_iIPversion() , m_szPayloadSize() , m_bClosing(false) - , m_LSLock() - , m_pListener(NULL) , m_pRendezvousQueue(NULL) , m_vNewEntry() , m_IDLock() @@ -1404,11 +1402,13 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, int listener_ret = SRT_REJ_UNKNOWN; bool have_listener = false; { - ScopedLock cg(m_LSLock); - if (m_pListener) + SharedLock shl(m_pListener); + CUDT* pListener = m_pListener.getPtrNoLock(); + + if (pListener) { - LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << m_pListener->socketID()); - listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); + LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << pListener->socketID()); + listener_ret = pListener->processConnectRequest(addr, unit->m_Packet); // This function does return a code, but it's hard to say as to whether // anything can be done about it. In case when it's stated possible, the @@ -1690,21 +1690,15 @@ int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) int srt::CRcvQueue::setListener(CUDT* u) { - ScopedLock lslock(m_LSLock); - - if (NULL != m_pListener) + if (!m_pListener.set(u)) return -1; - m_pListener = u; return 0; } void srt::CRcvQueue::removeListener(const CUDT* u) { - ScopedLock lslock(m_LSLock); - - if (u == m_pListener) - m_pListener = NULL; + m_pListener.clearIf(u); } void srt::CRcvQueue::registerConnector(const SRTSOCKET& id, diff --git a/srtcore/queue.h b/srtcore/queue.h index dd68a7721..43219736f 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -554,9 +554,8 @@ class CRcvQueue void storePktClone(int32_t id, const CPacket& pkt); private: - 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 + sync::CSharedObjectPtr 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 sync::Mutex m_IDLock; From bcc2f21ec75fcb10a8185857f3bc323bb19619da Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 7 Aug 2024 13:15:15 +0200 Subject: [PATCH 68/85] [docs] Added Rejection Reasons document (#2762). Co-authored-by: stevomatthews --- docs/API/API-functions.md | 2 + docs/API/rejection-codes.md | 473 ++++++++++++++++++++++++++++++++ docs/README.md | 1 + docs/features/access-control.md | 193 +------------ 4 files changed, 477 insertions(+), 192 deletions(-) create mode 100644 docs/API/rejection-codes.md diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 54895fe15..74fbc506f 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -174,6 +174,8 @@ Since SRT v1.5.0. | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | | | | | +See the full list in [Rejection Reason Codes](./rejection-codes.md). +

Error Codes

| *Error Code* | *Description* | diff --git a/docs/API/rejection-codes.md b/docs/API/rejection-codes.md new file mode 100644 index 000000000..0b8418b5f --- /dev/null +++ b/docs/API/rejection-codes.md @@ -0,0 +1,473 @@ +# SRT Rejection Codes + +This document provides an overview of the rejection (error) codes used by and supported within SRT and SRT-based applications. For information on other types of error codes refer to the [API Socket Options](./docs/API/API-socket-options.md) document. + +[:arrow_down:   Jump to list of rejection codes](#api-function-rejection-codes) + + +## Summary of Rejection Codes + +Rejection codes are used in the SRT API, and are transferred on the wire as a part of a Handshake packet (refer to the `Handshake Type` field of the [Handshake](./docs/features/handshake.md) packet). +The rejection codes are divided into several ranges: + + - SRT internal + - Predefined application level codes + - User defined (custom) codes + +In the SRT API these ranges are marked with the following constants (preprocessor definitions): + + - `SRT_REJC_INTERNAL` = 0 + - `SRT_REJC_PREDEFINED` = 1000 + - `SRT_REJC_USERDEFINED` = 2000 + +When transferred on the wire, the API value is incremented by 1000 to become the `Handshake Type` field value. In the following sections the values of rejection reason codes are given in accordance with the API values. + + +### SRT Internal Rejection Codes + +Defined in [**srt.h**](srtcore/srt.h), these codes provide the reason why a connection is rejected by SRT. They cover the reserved range 0 - 999 (below `SRT_REJC_PREDEFINED`). These codes cannot be used by applications to report a rejection reason. + +Naming: `SRT_REJ_*` + + - `SRT_REJ_UNKNOWN` = 0 + - `SRT_REJ_SYSTEM` = 1 + - ... + - `SRT_REJ_CRYPTO` = 17 +See [the list below](#api-function-rejection-codes) for details. + +### Extended Rejection Codes + +As defined in [**access_control.h**](srtcore/access_control.h), these are standard server error codes including those adopted from HTTP. They provide the reason why an application rejects a connection. The value is expected to be set by an application via the listener callback if it wants to reject an incoming connection request. These codes cover the reserved range 1000 - 1999 (`SRT_REJC_PREDEFINED` - `SRT_REJC_USERDEFINED`). + + +Subranges (1000 + value): + + - **0 - 99**: Reserved for unique SRT-specific codes (unused by HTTP) + - **100 - 399**: Info, Success, and Redirection in HTTP (unused by SRT) + - **400 - 599**: Client and server errors in HTTP (adopted by SRT) + +Naming: `SRT_REJX_*` + +Example: + + - `SRT_REJX_KEY_NOTSUP` (1001): The key used in the StreamID keyed string is not supported by the service. + - `SRT_REJX_BAD_REQUEST` (1400) + - ... + +### User Defined Rejection Codes + +These codes can be freely defined by an application. They can be custom codes, not adopted by other vendors. For example, `2005: ā€œMakito license expiredā€`. They cover the reserved range 2000 - 2999 (higher than `SRT_REJC_USERDEFINED`). + + +## API Function Rejection Codes + +SRT's API function rejection codes refer to system-level error conditions caused by SRT-specific settings or operating conditions. They are uninfluenced by application-related events, and applications are not permitted to use or simulate these codes. + +The table below lists the rejection codes as defined in [**srt.h**](srtcore/srt.h) (click the *Rejection Reason* link to view a complete description). + +| *Code* | *Rejection Reason* | *Since* | *Description* | +|:------:|:-------------------------------------------------- |:-------:|:-------------------------------------------------------------------------------------------------------------- | +| 0 | [SRT_REJ_UNKNOWN](#SRT_REJ_UNKNOWN) | 1.3.4 | Fallback value for cases where connection is not rejected. | +| 1 | [SRT_REJ_SYSTEM](#SRT_REJ_SYSTEM) | 1.3.4 | System function reported a failure. | +| 2 | [SRT_REJ_PEER](#SRT_REJ_PEER) | 1.3.4 | Connection rejected by peer, with no additional details. | +| 3 | [SRT_REJ_RESOURCE](#SRT_REJ_RESOURCE) | 1.3.4 | Problem with resource allocation (usually memory). | +| 4 | [SRT_REJ_ROGUE](#SRT_REJ_ROGUE) | 1.3.4 | Data sent by one party cannot be interpreted. | +| 5 | [SRT_REJ_BACKLOG](#SRT_REJ_BACKLOG) | 1.3.4 | Listener's backlog has been exceeded. | +| 6 | [SRT_REJ_IPE](#SRT_REJ_IPE) | 1.3.4 | Internal Program Error. | +| 7 | [SRT_REJ_CLOSE](#SRT_REJ_CLOSE) | 1.3.4 | Listener socket received a request as it is being closed. | +| 8 | [SRT_REJ_VERSION](#SRT_REJ_VERSION) | 1.3.4 | Minimum version requirement for a connection not satisfied by one party. | +| 9 | [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | 1.3.4 | Rendezvous cookie collision. | +| 10 | [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | 1.3.4 | Both parties have defined connection passphrases that differ. | +| 11 | [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one party has set up a connection password. | +| 12 | [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | 1.3.4 | [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties. | +| 13 | [SRT_REJ_CONGESTION](#SRT_REJ_CONGESTION) | 1.3.4 | Incompatible congestion-controller type. | +| 14 | [SRT_REJ_FILTER](#SRT_REJ_FILTER) | 1.3.4 | [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option is different on both connection parties. | +| 15 | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | Group type or group settings are incompatible between connection parties. | +| 16 | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | Connection not rejected, but timed out. | +| 17 | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | Connection rejected due to unsupported or mismatching encryption mode. | +| | | | | + + +## Access Control Rejection Codes + +SRT's access control rejection codes are intended for use by applications to forcefully reject connections in SRT listener callbacks. They are intended only as a guide to promote standardization. If they are used in an application, a description of their specific implementation should be published (the descriptions in this documentation are not definitive). + +The table below lists the rejection codes as defined in [**access_control.h**](srtcore/access_control.h) (click the *Rejection Reason* link to view a complete description). + +| *Code* | *Rejection Reason* | *Since* | *Description* | +|:------:|:------------------------------------------------- |:-------:|:-------------------------------------------------------------------------------------------------------------- | +| 1000 | [SRT_REJX_FALLBACK](#SRT_REJX_FALLBACK) | 1.4.2 | Callback handler has interrupted an incoming connection. | +| 1001 | [SRT_REJX_KEY_NOTSUP](#SRT_REJX_KEY_NOTSUP) | 1.4.2 | Key specified in StreamID string not supported by application. | +| 1002 | [SRT_REJX_FILEPATH](#SRT_REJX_FILEPATH) | 1.4.2 | Resource type designates file where path has wrong syntax or is not found. | +| 1003 | [SRT_REJX_HOSTNOTFOUND](#SRT_REJX_HOSTNOTFOUND) | 1.4.2 | The host specified in the `h` key cannot be identified. | +| 1400 | [SRT_REJX_BAD_REQUEST](#SRT_REJX_BAD_REQUEST) | 1.4.2 | General syntax error. | +| 1401 | [SRT_REJX_UNAUTHORIZED](#SRT_REJX_UNAUTHORIZED) | 1.4.2 | Authentication failed; client unauthorized to access the resource. | +| 1402 | [SRT_REJX_OVERLOAD](#SRT_REJX_OVERLOAD) | 1.4.2 | Server load too heavy to process request, or credit limit exceeded. | +| 1403 | [SRT_REJX_FORBIDDEN](#SRT_REJX_FORBIDDEN) | 1.4.2 | Access denied to the resource for any reason. | +| 1404 | [SRT_REJX_NOTFOUND](#SRT_REJX_NOTFOUND) | 1.4.2 | Resource specified by `r` and `h` keys cannot be found. | +| 1405 | [SRT_REJX_BAD_MODE](#SRT_REJX_BAD_MODE) | 1.4.2 | Mode specified in the `m` key in StreamID is not supported for this request. | +| 1406 | [SRT_REJX_UNACCEPTABLE](#SRT_REJX_UNACCEPTABLE) | 1.4.2 | Unavailable parameters in `StreamID`, or `m=publish` data format not supported. | +| 1409 | [SRT_REJX_CONFLICT](#SRT_REJX_CONFLICT) | 1.4.2 | Resource specified by `r` and `h` keys is locked for modification. | +| 1415 | [SRT_REJX_NOTSUP_MEDIA](#SRT_REJX_NOTSUP_MEDIA) | 1.4.2 | Media type not supported by the application. | +| 1423 | [SRT_REJX_LOCKED](#SRT_REJX_LOCKED) | 1.4.2 | Resource is locked against any access. | +| 1424 | [SRT_REJX_FAILED_DEPEND](#SRT_REJX_FAILED_DEPEND) | 1.4.2 | Dependent entity for the request is not present. | +| 1500 | [SRT_REJX_ISE](#SRT_REJX_ISE) | 1.4.2 | Internal server error. | +| 1501 | [SRT_REJX_UNIMPLEMENTED](#SRT_REJX_UNIMPLEMENTED) | 1.4.2 | Request not supported by current version of the service. | +| 1502 | [SRT_REJX_GW](#SRT_REJX_GW) | 1.4.2 | Target endpoint rejected connection from gateway server | +| 1503 | [SRT_REJX_DOWN](#SRT_REJX_DOWN) | 1.4.2 | Service is down for maintenance. | +| 1505 | [SRT_REJX_VERSION](#SRT_REJX_VERSION) | 1.4.2 | SRT application version not supported. | +| 1507 | [SRT_REJX_NOROOM](#SRT_REJX_NOROOM) | 1.4.2 | Data stream cannot be archived due to lack of storage space. | +| | | | | + + +**NOTE**: SRT rejection codes follow this prefix convention: + + - `SRT_REJ`: standard rejection codes from SRT API functions (0 - 99) + - `SRT_REJC`: mark the border values between ranges. + - `SRT_REJX`: extended rejection codes (code values above 1000).above)?* + + +## API Function Rejection Reasons + + +#### SRT_REJ_UNKNOWN + +A fallback value for cases when there was no connection rejected. + + +#### SRT_REJ_SYSTEM + +One system function reported a failure. Usually this means some system +error or lack of system resources to complete the task. + + +#### SRT_REJ_PEER + +The connection has been rejected by the peer, but no further details are available. +This usually means that the peer doesn't support rejection reason reporting. + + +#### SRT_REJ_RESOURCE + +A problem with resource allocation (usually memory). + + +#### SRT_REJ_ROGUE + +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. + + +#### SRT_REJ_BACKLOG + +The listener's backlog has exceeded its queue limit (there are many other callers +waiting for the opportunity to be connected and the "wait queue" has +reached its limit). + + +#### SRT_REJ_IPE + +Internal Program Error. This should not happen during normal usage. It +usually indicates a bug in the software (although this can be reported by both +local and foreign hosts). + + +#### SRT_REJ_CLOSE + +The listener socket was able to receive the request, but is currently +being closed. It's likely that the next request will result in a timeout. + + +#### SRT_REJ_VERSION + +One party in the connection has set up a minimum version that is required for +that connection, but the other party doesn't satisfy this requirement. + + +#### SRT_REJ_RDVCOOKIE + +Rendezvous cookie collision. Normally, the probability that this will happen is +negligible. However, it *can* result from a misconfiguration when, in attempting +to make a rendezvous connection, both parties try to bind to the same IP address, +or both are local addresses of the same host. In such a case the sent handshake +packets are returned to the same host as if they were sent by the peer (i.e. a +party is sending to itself). In such situations, this reject reason will be +reported for every attempt. + + +#### SRT_REJ_BADSECRET + +Both parties have defined a passphrase for a connection, but they differ. + + +#### SRT_REJ_UNSECURE + +Only one connection party has set up a password. See also the +[`SRTO_ENFORCEDENCRYPTION`](API-socket-options.md#SRTO_ENFORCEDENCRYPTION) flag. + + +#### SRT_REJ_MESSAGEAPI + +The value of the [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) +flag is different on both parties in a connection. + + +#### SRT_REJ_CONGESTION + +The [`SRTO_CONGESTION`](API-socket-options.md#SRTO_CONGESTION)option has +been set up differently on both parties in a connection. + + +#### SRT_REJ_FILTER + +The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option +has been set differently on both parties in a connection. + + +#### SRT_REJ_GROUP + +The group type or some group settings are incompatible between connection parties. +While every connection within a bonding group may have different target addresses, +they should all designate the same endpoint and the same SRT application. If this +condition isn't satisfied, then the peer will respond with a different peer group +ID for the connection that is trying to contact a machine/application that is +completely different from the existing connections in the bonding group. + + +#### SRT_REJ_TIMEOUT + +The connection wasn't rejected, but it timed out. This code is always sent on +a connection timeout, but this is the only way to get this state in non-blocking +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` +constants. Note that the number space from the value of `SRT_REJC_PREDEFINED` +and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus +adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by +the application. + +#### SRT_REJ_CRYPTO + +The connection was rejected due to a mismatch in crypto modes. See `SRTO_CRYPTOMODE`. + +[:arrow_up:   Back to top](#srt-rejection-codes) + + +## Access Control Rejection Reasons + +An SRT listener callback handler can decide to reject an incoming connection. +Under normal circumstances, the rejection code is predefined as `SRT_REJ_RESOURCE`. +The handler can, however, set its own rejection code. There are two numbered spaces +intended for this purpose (as the range below `SRT_REJC_PREDEFINED` is reserved +for internal codes): + +- `SRT_REJC_PREDEFINED` and above: These are predefined errors. Errors from this +range (that is, below `SRT_REJC_USERDEFINED`) have their definitions provided in +the `access_control.h` public header file. The intention is that applications +using these codes understand the situations they describe in a standard way. + +- `SRT_REJC_USERDEFINED` and above: These are errors that are freely defined by +the application. Codes from this range can be only understood if each application +knows the code definitions of the other. These codes should be used only after +making sure that the applications at either end of a connection understand them. + +The intention here is for the predefined codes to be consistent with the HTTP +standard codes. Such code can be set by using the [`srt_setrejectreason`](docs/api/api-functions.md#srt-setrejectreason) function. + +The SRT-specific codes are: + +#### SRT_REJX_FALLBACK + +This code should be set by the callback handler in the beginning in case +the application needs to be informed that the callback handler +actually has interpreted the incoming connection, but hasn't set a +more appropriate code describing the situation. + +#### SRT_REJX_KEY_NOTSUP + +Indicates there was a key specified in the StreamID string that this application +doesn't support. Note that it's not obligatory for the application to +react this way - it may chose to ignore unknown keys completely, or +to have some keys in the ignore list (which it won't interpret, but tolerate) +while rejecting any others. It is also up to the application +to decide to return this specific error, or more generally report +the syntax error with `SRT_REJX_BAD_REQUEST`. + +#### SRT_REJX_FILEPATH + +The resource type designates a file, and the path either has the wrong syntax +or is not found. In the case where `t=file`, the path should be specified under +the `r` key, and the file specified there must be able to be saved this way. +It's up to the application to decide how to treat this path, how to parse it, +and what this path specifically means. For the `r` key, the application should +at least handle the single filename, and have storage space available to save +it (provided a file of the same name does not already exist there). The +application should decide whether and how to handle all other situations (like +directory path, special markers in the path to be interpreted by the +application, etc.), or to report this error. + +#### SRT_REJX_HOSTNOTFOUND + +The host specified in the `h` key cannot be identified. The `h` key is +generally for a situation when you have multiple DNS names for a host, +so an application may want to extract the name from the URI and set it +to the `h` key so that the application can distinguish the request also by +the target host name. The application may, however, limit the number of +recognized services by host name to some predefined names and not +handle the others, even if this is properly resolved by DNS. In this +case it should report this error. + +The other error codes are HTTP codes adapted for SRT: + +#### SRT_REJX_BAD_REQUEST + +General syntax error. This can be reported in any case when parsing +the StreamID contents failed, or it cannot be properly interpreted. + +#### SRT_REJX_UNAUTHORIZED + +Authentication failed, which makes the client unauthorized to access the +resource. This error, however, confirms that the syntax is correct and +the resource has been properly identified. Note that this cannot be +reported when you use a simple user-password authentication +method because in this case the password is verified only after the +listener callback handler accepts the connection. This error is rather +intended to be reported in the case of `t=auth` when the authentication +process has generated some valid session ID, but then the session +connection has specified a resource that is not within the frames +of that authentication. + +#### SRT_REJX_OVERLOAD + +The server is too heavily loaded to process the request, or the credit limit +for accessing the service and the resource has been exceeded. +In HTTP the description mentions payment for a service, but +it is also used by some services for general "credit" management +for a client. In SRT it should be used when the service is doing +any kind of credit management to limit access to selected clients +that "have" enough credit, even if the credit is something the client +can recharge itself, or that can be granted depending on available +service resources. + +#### SRT_REJX_FORBIDDEN + +Access denied to the resource for any reason. This error is +independent of an authorization or authentication error (as reported +by `SRT_REJX_UNAUTHORIZED`). The application can decide which +is more appropriate. This error is usually intended for +a resource that should only be accessed after a successful +authorization over a separate auth-only connection, where the query +in StreamID has correctly specified the resource identity and mode, +but the session ID (in the `s` key) is either (a) not specified, or +(b) specifies a valid session, but the authorization region for this +session does not include the specified resource. + +#### SRT_REJX_NOTFOUND + +The resource specified in the `r` key (in combination with the `h` key) +is not found at this time. This error should be only reported if the +information about resource accessibility is allowed to be publicly +visible. Otherwise, the application might report authorization +errors. + +#### SRT_REJX_BAD_MODE + +The mode specified in the `m` key in StreamID is not supported for this request. +This may apply to read-only or write-only resources, as well when interactive +(bidirectional) access is not valid for a resource. + +#### SRT_REJX_UNACCEPTABLE + +Applies when the parameters specified in StreamID cannot be satisfied for the +requested resource, or when `m=publish` but the data format is not acceptable. +This is a general error reporting an unsupported format for data that appears to +be wrong when sending, or a restriction on the data format (as specified in the +details of the resource specification) such that it cannot be provided +when receiving. + +#### SRT_REJX_CONFLICT + +The resource being accessed (as specified by `r` and `h` keys) is locked for +modification. This error should only be reported for `m=publish` when the +resource being accessed is read-only because another client (not necessarily +connected through SRT): + +- is currently publishing into this resource +- has reserved this resource ID for publishing + +Note that this error should be reported when there is no other reason for +having a problem accessing the resource. + +#### SRT_REJX_NOTSUP_MEDIA + +The media type is not supported by the application. The media type is +specified in the `t` key. The currently standard types are +`stream`, `file` and `auth`. An application may extend this list, and +is not obliged to support all of the standard types. + +#### SRT_REJX_LOCKED + +The resource being accessed is locked against any access. This is similar to +`SRT_REJX_CONFLICT`, but in this case the resource is locked for reading +and writing. This is for when the resource should be shown as existing and +available to the client, but access is temporarily blocked. + +#### SRT_REJX_FAILED_DEPEND + +The dependent entity for the request is not present. In this case the +dependent entity is the session, which should be specified in the `s` +key. This means that the specified session ID is nonexistent, or it +has already expired. + +#### SRT_REJX_ISE + +Internal server error. This is for a general case when a request has +been correctly verified, with no related problems found, but an +unexpected error occurs after the processing of the request has started. + +#### SRT_REJX_UNIMPLEMENTED + +The request was correctly recognized, but the current software version +of the service (be it SRT or any other software component) doesn't +support it. This should be reported for a case where some features to +be specified in the StreamID request are supposed to be supported in a +predictable future, but the current version of the server does not +support it, or the support for this feature in this version has been +temporarily blocked. This shouldn't be reported for existing features that are +being deprecated, or older features that are no longer supported +(for this case the general `SRT_REJX_BAD_REQUEST` is more appropriate). + +#### SRT_REJX_GW + +The server acts as a gateway and the target endpoint rejected the +connection. The reason the connection was rejected is unspecified. +The gateway cannot forward the original rejection code from the +target endpoint because this would suggest the error was on the +gateway itself. Use this error with some other mechanism to report +the original target error, if possible. + +#### SRT_REJX_DOWN + +The service is down for maintenance. This can only be reported +when the service has been temporarily replaced by a stub that is only +reporting this error, while the real service is down for maintenance. + +#### SRT_REJX_VERSION + +Application version not supported. This can refer to an application feature +that is unsupported (possibly from an older SRT version), or to a feature +that is no longer supported because of backward compatibility requirements. + +#### SRT_REJX_NOROOM + +The data stream cannot be archived due to a lack of storage space. This is +reported when a request to send a file or a live stream to be archived is +unsuccessful. Note that the length of a file transmission is usually +pre-declared, so this error can be reported early. It can also be reported when +the stream is of undefined length, and there is no more storage space +available. + + +[:arrow_up:   Back to top](#srt-rejection-codes) diff --git a/docs/README.md b/docs/README.md index 9e3bdb198..8c07ee515 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ | [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 Rejection Codes](API/rejections-codes.md) | [API](API/) | [rejection-codes.md](API/rejection-codes.md) | The list of SRT rejections codes. | | [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. | | | | | | diff --git a/docs/features/access-control.md b/docs/features/access-control.md index 99fe891fa..6375802b2 100644 --- a/docs/features/access-control.md +++ b/docs/features/access-control.md @@ -154,198 +154,7 @@ standard codes. Therefore the following sub-ranges are used: Such a code can be set by using the `srt_setrejectreason` function. -The SRT-specific codes are: - -#### SRT_REJX_FALLBACK - -This code should be set by the callback handler in the beginning in case -the application needs to be informed that the callback handler -actually has interpreted the incoming connection, but hasn't set a -more appropriate code describing the situation. - -#### SRT_REJX_KEY_NOTSUP - -Indicates there was a key specified in the StreamID string that this application -doesn't support. Note that it's not obligatory for the application to -react this way - it may chose to ignore unknown keys completely, or -to have some keys in the ignore list (which it won't interpret, but tolerate) -while rejecting any others. It is also up to the application -to decide to return this specific error, or more generally report -the syntax error with `SRT_REJX_BAD_REQUEST`. - -#### SRT_REJX_FILEPATH - -The resource type designates a file, and the path either has the wrong syntax -or is not found. In the case where `t=file`, the path should be specified under -the `r` key, and the file specified there must be able to be saved this way. -It's up to the application to decide how to treat this path, how to parse it, -and what this path specifically means. For the `r` key, the application should -at least handle the single filename, and have storage space available to save -it (provided a file of the same name does not already exist there). The -application should decide whether and how to handle all other situations (like -directory path, special markers in the path to be interpreted by the -application, etc.), or to report this error. - -#### SRT_REJX_HOSTNOTFOUND - -The host specified in the `h` key cannot be identified. The `h` key is -generally for a situation when you have multiple DNS names for a host, -so an application may want to extract the name from the URI and set it -to `h` key so that the application can distinguish the request also by -the target host name. The application may however limit the number of -recognized services by host name to some predefined names and not -handle the others, even if this is properly resolved by DNS. In this -case it should report this error. - -The other error codes are HTTP codes adopted for SRT: - -#### SRT_REJX_BAD_REQUEST - -General syntax error. This can be reported in any case when parsing -the StreamID contents failed, or it cannot be properly interpreted. - -#### SRT_REJX_UNAUTHORIZED - -Authentication failed, which makes the client unauthorized to access the -resource. This error, however, confirms that the syntax is correct and -the resource has been properly identified. Note that this cannot be -reported when you use a simple user-password authentication -method because in this case the password is verified only after the -listener callback handler accepts the connection. This error is rather -intended to be reported in case of `t=auth` when the authentication -process has generated some valid session ID, but then the session -connection has specified a resource that is not within the frames -of that authentication. - -#### SRT_REJX_OVERLOAD - -The server is too heavily loaded to process your request, or you -have exceeded credits for accessing the service and the resource. -In HTTP the description mentions payment for a service, but -it is also used by some services for general "credit" management -for a client. In SRT it should be used when your service is doing -any kind of credit management to limit access to selected clients -that "have" enough credit, even if the credit is something the client -can recharge itself, or that can be granted depending on available -service resources. - -#### SRT_REJX_FORBIDDEN - -Access denied to the resource for any reason. This error is -independent of an authorization or authentication error (as reported -by `SRT_REJX_UNAUTHORIZED`). The application can decide which -is more appropriate. This error is usually intended for -a resource that should only be accessed after a successful -authorization over a separate auth-only connection, where the query -in StreamID has correctly specified the resource identity and mode, -but the session ID (in the `s` key) is either (a) not specified, or (b) does -specify a valid session, but the authorization region for this -session does not embrace the specified resource. - -#### SRT_REJX_NOTFOUND - -The resource specified in the `r` key (in combination with the `h` key) -is not found at this time. This error should be only reported if the -information about resource accessibility is allowed to be publicly -visible. Otherwise the application might report authorization -errors. - -#### SRT_REJX_BAD_MODE - -The mode specified in the `m` key in StreamID is not supported for this request. -This may apply to read-only or write-only resources, as well for when interactive -(bidirectional) access is not valid for a resource. - -#### SRT_REJX_UNACCEPTABLE - -Applies when the parameters specified in StreamID cannot be satisfied for the -requested resource, or when `m=publish` but the data format is not acceptable. -This is a general error reporting an unsupported format for data that appears to -be wrong when sending, or a restriction on the data format (as specified in the -details of the resource specification) such that it cannot be provided -when receiving. - -#### SRT_REJX_CONFLICT - -The resource being accessed (as specified by `r` and `h` keys) is locked for -modification. This error should only be reported for `m=publish` when the -resource being accessed is read-only because another client (not necessarily -connected through SRT): - -- is currently publishing into this resource -- has reserved this resource ID for publishing - -Note that this error should be reported when there is no other reason for -having a problem accessing the resource. - -#### SRT_REJX_NOTSUP_MEDIA - -The media type is not supported by the application. The media type is -specified in the `t` key. The currently standard types are -`stream`, `file` and `auth`. An application may extend this list, and -is not obliged to support all of the standard types. - -#### SRT_REJX_LOCKED - -The resource being accessed is locked against any access. This is similar to -`SRT_REJX_CONFLICT`, but in this case the resource is locked for reading -and writing. This is for when the resource should be shown as existing and -available to the client, but access is temporarily blocked. - -#### SRT_REJX_FAILED_DEPEND - -The dependent entity for the request is not present. In this case the -dependent entity is the session, which should be specified in the `s` -key. This means that the specified session ID is nonexistent or it -has already expired. - -#### SRT_REJX_ISE - -Internal server error. This is for a general case when a request has -been correctly verified, with no related problems found, but an -unexpected error occurs after the processing of the request has started. - -#### SRT_REJX_UNIMPLEMENTED - -The request was correctly recognized, but the current software version -of the service (be it SRT or any other software component) doesn't -support it. This should be reported for a case, when some features to -be specified in the StreamID request are supposed to be supported in a -predictable future, but the current version of the server does not -support it, or the support for this feature in this version has been -temporarily blocked. This shouldn't be reported for existing features that are -being deprecated, or older features that are no longer supported -(for this case the general `SRT_REJX_BAD_REQUEST` is more appropriate). - -#### SRT_REJX_GW - -The server acts as a gateway and the target endpoint rejected the -connection. The reason the connection was rejected is unspecified. -The gateway cannot forward the original rejection code from the -target endpoint because this would suggest the error was on the -gateway itself. Use this error with some other mechanism to report -the original target error, if possible. - -#### SRT_REJX_DOWN - -The service is down for maintenance. This can only be reported -when the service has been temporarily replaced by a stub that is only -reporting this error, while the real service is down for maintenance. - -#### SRT_REJX_VERSION - -Application version not supported. This can refer to an application feature -that is unsupported (possibly from an older SRT version), or to a feature -that is no longer supported because of backward compatibility requirements. - -#### SRT_REJX_NOROOM - -The data stream cannot be archived due to a lack of storage space. This is -reported when a request to send a file or a live stream to be archived is -unsuccessful. Note that the length of a file transmission is usually -pre-declared, so this error can be reported early. It can also be reported when -the stream is of undefined length, and there is no more storage space -available. +See the list of rejection codes in the [Rejection Codes](../API/rejection-codes.md) document. ## Example From 4270a11a73a57f1f002609123f2eb31b14a9b0c0 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 7 Aug 2024 10:57:55 +0200 Subject: [PATCH 69/85] [core] Fixed a data race on listener's config. Co-authored-by: yomnes0 --- srtcore/api.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7eda3f2bf..e64c60f29 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -537,6 +537,8 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, try { + // Protect the config of the listener socket from a data race. + ScopedLock lck(ls->core().m_ConnectionLock); ns = new CUDTSocket(*ls); // No need to check the peer, this is the address from which the request has come. ns->m_PeerAddr = peer; From b8a2822dc87f61a967be607f0b7592029a4b9e99 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Aug 2024 10:33:29 +0200 Subject: [PATCH 70/85] [core] Made CSndQueue::m_counter atomic. --- srtcore/queue.cpp | 2 +- srtcore/queue.h | 2 +- srtcore/sync.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 7adc9aa47..d5127faaf 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -444,7 +444,7 @@ int srt::CSndQueue::sockoptQuery(int level, int type) const } #if ENABLE_LOGGING -int srt::CSndQueue::m_counter = 0; +srt::sync::atomic srt::CSndQueue::m_counter(0); #endif void srt::CSndQueue::init(CChannel* c, CTimer* t) diff --git a/srtcore/queue.h b/srtcore/queue.h index 43219736f..132b670b3 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -469,7 +469,7 @@ class CSndQueue private: #if ENABLE_LOGGING - static int m_counter; + static srt::sync::atomic m_counter; #endif CSndQueue(const CSndQueue&); diff --git a/srtcore/sync.h b/srtcore/sync.h index e8b7444bd..9d282304c 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -540,7 +540,7 @@ class SRT_ATTR_SCOPED_CAPABILITY ExclusiveLock m_mutex.lock(); } - SRT_ATTR_RELEASE() + SRT_ATTR_RELEASE(m_mutex) ~ExclusiveLock() { m_mutex.unlock(); } private: From 91f2c59902c55aa70fc83bfb0a7dbdfbf90f3d4f Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 8 Aug 2024 16:57:21 +0200 Subject: [PATCH 71/85] [core] Fixed THREAD_STATE_INIT usage in CRcvQueue (#3001). --- srtcore/queue.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index d5127faaf..31a1460fd 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -504,11 +504,9 @@ void* srt::CSndQueue::worker(void* param) { CSndQueue* self = (CSndQueue*)param; -#if ENABLE_LOGGING - THREAD_STATE_INIT(("SRT:SndQ:w" + Sprint(m_counter)).c_str()); -#else - THREAD_STATE_INIT("SRT:SndQ:worker"); -#endif + std::string thname; + ThreadName::get(thname); + THREAD_STATE_INIT(thname.c_str()); #if defined(SRT_DEBUG_SNDQ_HIGHRATE) #define IF_DEBUG_HIGHRATE(statement) statement @@ -1206,11 +1204,9 @@ void* srt::CRcvQueue::worker(void* param) sockaddr_any sa(self->getIPversion()); int32_t id = 0; -#if ENABLE_LOGGING - THREAD_STATE_INIT(("SRT:RcvQ:w" + Sprint(m_counter)).c_str()); -#else - THREAD_STATE_INIT("SRT:RcvQ:worker"); -#endif + std::string thname; + ThreadName::get(thname); + THREAD_STATE_INIT(thname.c_str()); CUnit* unit = 0; EConnectStatus cst = CONN_AGAIN; From c5f166cbb4beff8da386cb3a38824f2a9f8581dc Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Mon, 12 Aug 2024 19:21:14 +0200 Subject: [PATCH 72/85] [apps] Fixed verbose linkage difference (#3002). --- CMakeLists.txt | 2 -- apps/verbose.cpp | 13 +++---------- apps/verbose.hpp | 13 +------------ 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6500e364b..9a2fc63d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1383,7 +1383,6 @@ if (ENABLE_APPS) # srt-multiplex temporarily blocked #srt_add_application(srt-multiplex ${VIRTUAL_srtsupport}) srt_add_application(srt-tunnel ${VIRTUAL_srtsupport}) - target_compile_definitions(srt-tunnel PUBLIC -DSRT_ENABLE_VERBOSE_LOCK) endif() if (ENABLE_TESTING) @@ -1421,7 +1420,6 @@ if (ENABLE_APPS) srt_add_testprogram(srt-test-relay) srt_make_application(srt-test-relay) - target_compile_definitions(srt-test-relay PUBLIC -DSRT_ENABLE_VERBOSE_LOCK) srt_add_testprogram(srt-test-multiplex) srt_make_application(srt-test-multiplex) diff --git a/apps/verbose.cpp b/apps/verbose.cpp index 58cf83fbe..4ea3b47dc 100644 --- a/apps/verbose.cpp +++ b/apps/verbose.cpp @@ -9,14 +9,13 @@ */ #include "verbose.hpp" +#include "sync.h" // srt::sync namespace Verbose { bool on = false; std::ostream* cverb = &std::cerr; -#if SRT_ENABLE_VERBOSE_LOCK - std::mutex vlock; -#endif + srt::sync::Mutex vlock; Log& Log::operator<<(LogNoEol) { @@ -28,19 +27,16 @@ namespace Verbose return *this; } -#if SRT_ENABLE_VERBOSE_LOCK Log& Log::operator<<(LogLock) { lockline = true; return *this; } -#endif Log::~Log() { if (on && !noeol) { -#if SRT_ENABLE_VERBOSE_LOCK if (lockline) { // Lock explicitly, as requested, and wait for the opportunity. @@ -48,7 +44,7 @@ namespace Verbose } else if (vlock.try_lock()) { - // Successfully locked, so unlock immediately, locking wasn't requeted. + // Successfully locked, so unlock immediately, locking wasn't requested. vlock.unlock(); } else @@ -62,15 +58,12 @@ namespace Verbose vlock.lock(); vlock.unlock(); } -#endif (*cverb) << std::endl; -#if SRT_ENABLE_VERBOSE_LOCK // If lockline is set, the lock was requested and WAS DONE, so unlock. // Otherwise locking WAS NOT DONE. if (lockline) vlock.unlock(); -#endif } } } diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 879d54086..80f3b306b 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -12,9 +12,6 @@ #define INC_SRT_VERBOSE_HPP #include -#if SRT_ENABLE_VERBOSE_LOCK -#include -#endif namespace Verbose { @@ -23,19 +20,15 @@ extern bool on; extern std::ostream* cverb; struct LogNoEol { LogNoEol() {} }; -#if SRT_ENABLE_VERBOSE_LOCK struct LogLock { LogLock() {} }; -#endif class Log { bool noeol = false; -#if SRT_ENABLE_VERBOSE_LOCK bool lockline = false; -#endif // Disallow creating dynamic objects - void* operator new(size_t); + void* operator new(size_t) = delete; public: @@ -50,9 +43,7 @@ class Log } Log& operator<<(LogNoEol); -#if SRT_ENABLE_VERBOSE_LOCK Log& operator<<(LogLock); -#endif ~Log(); }; @@ -99,8 +90,6 @@ inline void Verb(Args&&... args) // Manipulator tags static const Verbose::LogNoEol VerbNoEOL; -#if SRT_ENABLE_VERBOSE_LOCK static const Verbose::LogLock VerbLock; -#endif #endif From 1669542e050833873af3cde7229958bb2b5bc3ae Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 13 Aug 2024 10:15:08 +0200 Subject: [PATCH 73/85] [apps] Fixed atomic setting of locked line in Verb (#3004). --- apps/verbose.hpp | 16 +++++++++-- testing/srt-test-live.cpp | 52 +++++++++++++++++----------------- testing/srt-test-multiplex.cpp | 4 +-- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 80f3b306b..e3d20732a 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -12,6 +12,7 @@ #define INC_SRT_VERBOSE_HPP #include +#include "atomic.h" namespace Verbose { @@ -25,13 +26,17 @@ struct LogLock { LogLock() {} }; class Log { bool noeol = false; - bool lockline = false; + srt::sync::atomic lockline; // Disallow creating dynamic objects void* operator new(size_t) = delete; public: + Log() {} + Log(const Log& ) {} + + template Log& operator<<(const V& arg) { @@ -71,7 +76,7 @@ inline void Print(Log& ) {} template inline void Print(Log& out, Arg1&& arg1, Args&&... args) { - out << std::forward(arg1); + out << arg1; Print(out, args...); } @@ -87,6 +92,13 @@ inline void Verb(Args&&... args) Verbose::Print(log, args...); } +template +inline void Verror(Args&&... args) +{ + Verbose::ErrLog log; + Verbose::Print(log, args...); +} + // Manipulator tags static const Verbose::LogNoEol VerbNoEOL; diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 17f4020dc..1811220c8 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -268,7 +268,7 @@ bool CheckMediaSpec(const string& prefix, const vector& spec, string& w_ for (string& a: adrs) w_outspec += a + ","; - Verb() << "NOTE: " << prefix << " specification set as: " << (w_outspec); + Verb("NOTE: ", prefix, " specification set as: ", (w_outspec)); return true; } @@ -294,7 +294,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr int type; int size = sizeof type; srt_getsockflag(acpsock, SRTO_GROUPCONNECT, &type, &size); - Verb() << "listener: @" << acpsock << " - accepting " << (type ? "GROUP" : "SINGLE") << VerbNoEOL; + Verb("listener: @", acpsock, " - accepting ", (type ? "GROUP" : "SINGLE"), VerbNoEOL); if (type != 0) { SRT_GROUP_TYPE gt; @@ -302,12 +302,12 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { if (gt < Size(gtypes)) - Verb() << " type=" << gtypes[gt] << VerbNoEOL; + Verb(" type=", gtypes[gt], VerbNoEOL); else - Verb() << " type=" << int(gt) << VerbNoEOL; + Verb(" type=", int(gt), VerbNoEOL); } } - Verb() << " connection"; + Verb(" connection"); return 0; } @@ -317,7 +317,7 @@ extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const soc { if (hsv < 5) { - Verb() << "SrtUserPasswordHook: HS version 4 doesn't support extended handshake"; + Verb("SrtUserPasswordHook: HS version 4 doesn't support extended handshake"); return -1; } @@ -818,7 +818,7 @@ int main( int argc, char** argv ) return 0; } - Verb() << "MEDIA CREATION FAILED: " << x.what() << " - exiting."; + Verb("MEDIA CREATION FAILED: ", x.what(), " - exiting."); // Don't speak anything when no -v option. // (the "requested interrupt" will be printed anyway) @@ -847,10 +847,10 @@ int main( int argc, char** argv ) if (transmit_use_sourcetime && src->uri.type() != UriParser::SRT) { - Verb() << "WARNING: -st option is effective only if the target type is SRT"; + Verb("WARNING: -st option is effective only if the target type is SRT"); } - Verb() << "STARTING TRANSMISSION: '" << source_spec << "' --> '" << target_spec << "'"; + Verb("STARTING TRANSMISSION: '", source_spec, "' --> '", target_spec, "'"); // After the time has been spent in the creation // (including waiting for connection) @@ -876,41 +876,41 @@ int main( int argc, char** argv ) { if (stoptime == 0 && timeout != -1 ) { - Verb() << "[." << VerbNoEOL; + Verb("[.", VerbNoEOL); alarm(timeout); } else { alarm(0); } - Verb() << " << ... " << VerbNoEOL; + Verb(", ... ", VerbNoEOL); g_interrupt_reason = "reading"; const MediaPacket& data = src->Read(chunk); - Verb() << " << " << data.payload.size() << " -> " << VerbNoEOL; + Verb(", ", data.payload.size(), " -> ", VerbNoEOL); if ( data.payload.empty() && src->End() ) { - Verb() << "EOS"; + Verb("EOS"); break; } g_interrupt_reason = "writing"; tar->Write(data); if (stoptime == 0 && timeout != -1 ) { - Verb() << ".] " << VerbNoEOL; + Verb(".] ", VerbNoEOL); alarm(0); } if ( tar->Broken() ) { - Verb() << " OUTPUT broken"; + Verb(" OUTPUT broken"); break; } - Verb() << "sent"; + Verb("sent"); if (::transmit_int_state) { - Verror() << "\n (interrupted on request)"; + Verror("\n (interrupted on request)"); break; } @@ -922,7 +922,7 @@ int main( int argc, char** argv ) int remain = int(stoptime - final_delay - elapsed); if (remain < 0) { - Verror() << "\n (interrupted on timeout: elapsed " << elapsed << "s) - waiting " << final_delay << "s for cleanup"; + Verror("\n (interrupted on timeout: elapsed ", elapsed, "s) - waiting ", final_delay, "s for cleanup"); this_thread::sleep_for(chrono::seconds(final_delay)); break; } @@ -934,17 +934,17 @@ int main( int argc, char** argv ) if (!skip_flushing) { - Verror() << "(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n"; + Verror("(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n"); for (;;) { size_t still = tar->Still(); if (still == 0) { - Verror() << "(DEBUG) DEPLETED. Done.\n"; + Verror("(DEBUG) DEPLETED. Done.\n"); break; } - Verror() << "(DEBUG)... still " << still << " bytes (sleep 1s)\n"; + Verror("(DEBUG)... still ", still, " bytes (sleep 1s)\n"); this_thread::sleep_for(chrono::seconds(1)); } } @@ -952,16 +952,16 @@ int main( int argc, char** argv ) if (stoptime != 0 && ::timer_state) { - Verror() << "Exit on timeout."; + Verror("Exit on timeout."); } else if (::transmit_int_state) { - Verror() << "Exit on interrupt."; + Verror("Exit on interrupt."); // Do nothing. } else { - Verror() << "STD EXCEPTION: " << x.what(); + Verror("STD EXCEPTION: ", x.what()); } if ( crashonx ) @@ -969,7 +969,7 @@ int main( int argc, char** argv ) if (final_delay > 0) { - Verror() << "Waiting " << final_delay << "s for possible cleanup..."; + Verror("Waiting ", final_delay, "s for possible cleanup..."); this_thread::sleep_for(chrono::seconds(final_delay)); } if (stoptime != 0 && ::timer_state) @@ -979,7 +979,7 @@ int main( int argc, char** argv ) } catch (...) { - Verror() << "UNKNOWN type of EXCEPTION"; + Verror("UNKNOWN type of EXCEPTION"); if ( crashonx ) throw; diff --git a/testing/srt-test-multiplex.cpp b/testing/srt-test-multiplex.cpp index 62c7fbd3d..deb36554c 100644 --- a/testing/srt-test-multiplex.cpp +++ b/testing/srt-test-multiplex.cpp @@ -403,7 +403,7 @@ void Stall() ++i_next; if (i->has_quit) { - Verb() << "Found QUIT mediumpair: " << i->name << " - removing from base"; + Verb("Found QUIT mediumpair: ", i->name, " - removing from base"); i->Stop(); g_media_base.media.erase(i); } @@ -411,7 +411,7 @@ void Stall() if (g_media_base.media.empty()) { - Verb() << "All media have quit. Marking exit."; + Verb("All media have quit. Marking exit."); break; } } From cf4b0804bb795fb018755edc30f9903115c9fec4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 13 Aug 2024 10:42:14 +0200 Subject: [PATCH 74/85] [core] Apply lock on RCV buffer read-ready check. Co-authored-by: Sektor van Skijlen --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9715a209e..7067e195c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -7124,7 +7124,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ do { - if (stillConnected() && !timeout && !m_pRcvBuffer->isRcvDataReady(steady_clock::now())) + if (stillConnected() && !timeout && !isRcvBufferReady()) { /* Kick TsbPd thread to schedule next wakeup (if running) */ if (m_bTsbPd) From f7b32e7fc6767729b8a64cb66db72dce6a366ce4 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 13 Aug 2024 11:06:12 +0200 Subject: [PATCH 75/85] [core] Apply logger lock on its config. Co-authored-by: Sektor van Skijlen --- srtcore/logging.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/srtcore/logging.h b/srtcore/logging.h index 2ec5f46aa..3f4efb286 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -112,7 +112,7 @@ struct LogConfig std::ostream* log_stream; SRT_LOG_HANDLER_FN* loghandler_fn; void* loghandler_opaque; - srt::sync::Mutex mutex; + mutable srt::sync::Mutex mutex; int flags; LogConfig(const fa_bitset_t& efa, @@ -132,10 +132,10 @@ struct LogConfig } SRT_ATTR_ACQUIRE(mutex) - void lock() { mutex.lock(); } + void lock() const { mutex.lock(); } SRT_ATTR_RELEASE(mutex) - void unlock() { mutex.unlock(); } + void unlock() const { mutex.unlock(); } }; // The LogDispatcher class represents the object that is responsible for @@ -424,8 +424,10 @@ inline bool LogDispatcher::CheckEnabled() // when the enabler check is tested here. Worst case, the log // will be printed just a moment after it was turned off. const LogConfig* config = src_config; // to enforce using const operator[] + config->lock(); int configured_enabled_fa = config->enabled_fa[fa]; int configured_maxlevel = config->max_level; + config->unlock(); return configured_enabled_fa && level <= configured_maxlevel; } From 430a67afcb3ec145af084d9010fafba83e63f60a Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 13 Aug 2024 11:08:05 +0200 Subject: [PATCH 76/85] [core] Added IOVector constructor. Co-authored-by: Sektor van Skijlen --- srtcore/packet.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/packet.h b/srtcore/packet.h index 5094247b5..5a6d6eb15 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -74,6 +74,8 @@ class IOVector #endif { public: + IOVector() { set(NULL, 0); } + inline void set(void* buffer, size_t length) { #ifdef _WIN32 From bd071e19be98ce09adeeeacfe5f7ba4e8b4a6cbf Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 14 Aug 2024 14:07:20 +0200 Subject: [PATCH 77/85] [core] Fix the group option that should be taken from a socket (#2912). --- srtcore/group.cpp | 72 +++++++++++++++++++++++++++++++++++++++++-- srtcore/group.h | 2 ++ test/test_bonding.cpp | 27 ++++++++++++++++ test/test_crypto.cpp | 4 +-- 4 files changed, 101 insertions(+), 4 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d4598d7c1..ee28029ae 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -758,12 +758,15 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) w_optlen = sizeof(uint32_t); return; + case SRTO_KMSTATE: + *(uint32_t*)pw_optval = getGroupEncryptionState(); + w_optlen = sizeof(uint32_t); + return; + // Write-only options for security reasons or // options that refer to a socket state, that // makes no sense for a group. case SRTO_PASSPHRASE: - case SRTO_KMSTATE: - case SRTO_PBKEYLEN: case SRTO_KMPREANNOUNCE: case SRTO_KMREFRESHRATE: case SRTO_BINDTODEVICE: @@ -775,6 +778,19 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) default:; // pass on } + bool is_set_on_socket = false; + { + ScopedLock cg(m_GroupLock); + gli_t gi = m_Group.begin(); + if (gi != m_Group.end()) + { + // Return the value from the first member socket, if any is present + // Note: Will throw exception if the request is wrong. + gi->ps->core().getOpt(optname, (pw_optval), (w_optlen)); + is_set_on_socket = true; + } + } + // Check if the option is in the storage, which means that // it was modified on the group. @@ -783,12 +799,18 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) if (i == m_config.end()) { + // Already written to the target variable. + if (is_set_on_socket) + return; + // Not found, see the defaults if (!getOptDefault(optname, (pw_optval), (w_optlen))) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); return; } + // NOTE: even if is_set_on_socket, if it was also found in the group + // settings, overwrite with the value from the group. // Found, return the value from the storage. // Check the size first. @@ -799,6 +821,52 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) memcpy((pw_optval), &i->value[0], i->value.size()); } +SRT_KM_STATE CUDTGroup::getGroupEncryptionState() +{ + multiset kmstates; + { + ScopedLock lk (m_GroupLock); + + // First check the container. If empty, return UNSECURED + if (m_Group.empty()) + return SRT_KM_S_UNSECURED; + + for (gli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + CCryptoControl* cc = gi->ps->core().m_pCryptoControl.get(); + if (!cc) + continue; + SRT_KM_STATE gst = cc->m_RcvKmState; + // A fix to NOSECRET is because this is the state when agent has set + // no password, but peer did, and ENFORCEDENCRYPTION=false allowed + // this connection to be established. UNSECURED can't be taken in this + // case because this would suggest that BOTH are unsecured, that is, + // we have established an unsecured connection (which ain't true). + if (gst == SRT_KM_S_UNSECURED && cc->m_SndKmState == SRT_KM_S_NOSECRET) + gst = SRT_KM_S_NOSECRET; + kmstates.insert(gst); + } + } + + // Criteria are: + // 1. UNSECURED, if no member sockets, or at least one UNSECURED found. + // 2. SECURED, if at least one SECURED found (cut off the previous criteria). + // 3. BADSECRET otherwise, although return NOSECRET if no BADSECRET is found. + + if (kmstates.count(SRT_KM_S_UNSECURED)) + return SRT_KM_S_UNSECURED; + + // Now we have UNSECURED ruled out. Remaining may be NOSECRET, BADSECRET or SECURED. + // NOTE: SECURING is an intermediate state for HSv4 and can't occur in groups. + if (kmstates.count(SRT_KM_S_SECURED)) + return SRT_KM_S_SECURED; + + if (kmstates.count(SRT_KM_S_BADSECRET)) + return SRT_KM_S_BADSECRET; + + return SRT_KM_S_NOSECRET; +} + SRT_SOCKSTATUS CUDTGroup::getStatus() { typedef vector > states_t; diff --git a/srtcore/group.h b/srtcore/group.h index 247423da3..56f1456f8 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -313,6 +313,8 @@ class CUDTGroup void send_CheckValidSockets(); + SRT_KM_STATE getGroupEncryptionState(); + public: int recv(char* buf, int len, SRT_MSGCTRL& w_mc); diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index 241d26d99..c5e362e69 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -349,6 +349,14 @@ TEST(Bonding, Options) string pass = "longenoughpassword"; // passphrase should be ok. EXPECT_NE(srt_setsockflag(grp, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); + + uint32_t val = 16; + EXPECT_NE(srt_setsockflag(grp, SRTO_PBKEYLEN, &val, sizeof val), SRT_ERROR); + +#ifdef ENABLE_AEAD_API_PREVIEW + val = 1; + EXPECT_NE(srt_setsockflag(grp, SRTO_CRYPTOMODE, &val, sizeof val), SRT_ERROR); +#endif #endif int lat = 500; @@ -446,6 +454,25 @@ TEST(Bonding, Options) EXPECT_EQ(optsize, sizeof ohead); EXPECT_EQ(ohead, 12); +#if SRT_ENABLE_ENCRYPTION + + uint32_t kms = -1; + + EXPECT_NE(srt_getsockflag(grp, SRTO_KMSTATE, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(kms, int(SRT_KM_S_SECURED)); + + EXPECT_NE(srt_getsockflag(grp, SRTO_PBKEYLEN, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(kms, 16); + +#ifdef ENABLE_AEAD_API_PREVIEW + EXPECT_NE(srt_getsockflag(grp, SRTO_CRYPTOMODE, &kms, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(kms, 1); +#endif +#endif + // We're done, the thread can close connection and exit { // Make sure that the thread reached the wait() call. diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index f4fa7f614..47b18dd1a 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -44,7 +44,7 @@ namespace srt m_crypt.setCryptoKeylen(cfg.iSndCryptoKeyLen); cfg.iCryptoMode = CSrtConfig::CIPHER_MODE_AES_GCM; - EXPECT_EQ(m_crypt.init(HSD_INITIATOR, cfg, true), HaiCrypt_IsAESGCM_Supported() != 0); + EXPECT_TRUE(m_crypt.init(HSD_INITIATOR, cfg, true, HaiCrypt_IsAESGCM_Supported())); const unsigned char* kmmsg = m_crypt.getKmMsg_data(0); const size_t km_len = m_crypt.getKmMsg_size(0); @@ -53,7 +53,7 @@ namespace srt std::array km_nworder; NtoHLA(km_nworder.data(), reinterpret_cast(kmmsg), km_len); - m_crypt.processSrtMsg_KMREQ(km_nworder.data(), km_len, 5, kmout, kmout_len); + m_crypt.processSrtMsg_KMREQ(km_nworder.data(), km_len, 5, SrtVersion(1, 5, 3), kmout, kmout_len); } void TearDown() override From 49cab205dc9f56a672f1203ef26825d539f2c33c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 14 Aug 2024 14:53:54 +0200 Subject: [PATCH 78/85] [core] Include packet header in rexmit BW calculation. --- srtcore/buffer_tools.cpp | 5 +++-- srtcore/buffer_tools.h | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 3f7a77be6..7473e3824 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -234,7 +234,7 @@ void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes) } else { - m_iRateBps = sum.m_iBytesCount * 1000 / (iNumPeriods * SAMPLE_DURATION_MS); + m_iRateBps = (sum.m_iBytesCount + CPacket::HDR_SIZE * sum.m_iPktsCount) * 1000 / (iNumPeriods * SAMPLE_DURATION_MS); } HLOGC(bslog.Note, @@ -260,7 +260,8 @@ void CSndRateEstimator::addSample(const time_point& ts, int pkts, size_t bytes) int CSndRateEstimator::getCurrentRate() const { SRT_ASSERT(m_iCurSampleIdx >= 0 && m_iCurSampleIdx < NUM_PERIODS); - return (int) avg_iir<16, unsigned long long>(m_iRateBps, m_Samples[m_iCurSampleIdx].m_iBytesCount * 1000 / SAMPLE_DURATION_MS); + const Sample& s = m_Samples[m_iCurSampleIdx]; + return (int) avg_iir<16, unsigned long long>(m_iRateBps, (CPacket::HDR_SIZE * s.m_iPktsCount + s.m_iBytesCount) * 1000 / SAMPLE_DURATION_MS); } int CSndRateEstimator::incSampleIdx(int val, int inc) const diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index aacbd8310..88479827d 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -141,10 +141,11 @@ class CSndRateEstimator /// @param [in] bytes number of payload bytes in the sample. void addSample(const time_point& time, int pkts = 0, size_t bytes = 0); - /// Retrieve estimated bitrate in bytes per second + /// Retrieve estimated bitrate in bytes per second with 16-byte packet header. int getRate() const { return m_iRateBps; } - /// Retrieve estimated bitrate in bytes per second inluding the current sampling interval. + /// Retrieve estimated bitrate in bytes per second (with 16-byte packet header) + /// including the current sampling interval. int getCurrentRate() const; private: @@ -191,10 +192,10 @@ class CSndRateEstimator Sample m_Samples[NUM_PERIODS]; - time_point m_tsFirstSampleTime; //< Start time of the first sameple. + time_point m_tsFirstSampleTime; //< Start time of the first sample. int m_iFirstSampleIdx; //< Index of the first sample. int m_iCurSampleIdx; //< Index of the current sample being collected. - int m_iRateBps; // Input Rate in Bytes/sec + int m_iRateBps; //< Rate in Bytes/sec. }; } // namespace srt From 70e212754c4bd907f581666fb784d7ecf11cb200 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 16 Aug 2024 11:03:22 +0200 Subject: [PATCH 79/85] [core] Slowdown 'ACKACK out of order' log frequency (#3007). --- srtcore/core.cpp | 27 +++++++++++++++++++-------- srtcore/core.h | 14 ++++++++------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7067e195c..bae245392 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5965,7 +5965,7 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st #endif bool is_suppressed = IsSet(m_LogSlowDownExpired, BIT(logid)); - bool isnow = (m_tsLogSlowDown.load() + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow; + const bool isnow = (m_tsLogSlowDown[logid].load() + milliseconds_from(SRT_LOG_SLOWDOWN_FREQ_MS)) <= tnow; if (isnow) { // Theoretically this should prevent other calls of this function to take @@ -5977,11 +5977,11 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st // Note: it may happen that two threads could intermix one another between // the check and setting up, but this will at worst case set the slightly // later time again. - m_tsLogSlowDown.store(tnow); + m_tsLogSlowDown[logid].store(tnow); is_suppressed = false; - int supr = m_aSuppressedMsg[logid]; + const int supr = m_aSuppressedMsg[logid]; if (supr > 0) w_why = Sprint("++SUPPRESSED: ", supr); @@ -5989,7 +5989,7 @@ bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::st } else { - w_why = Sprint("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown.load())); + w_why = Sprint("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown[logid].load())); // Set YOUR OWN bit, atomically. m_LogSlowDownExpired |= uint8_t(BIT(logid)); ++m_aSuppressedMsg[logid]; @@ -8688,10 +8688,21 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr { if (ctrlpkt.getAckSeqNo() > (m_iAckSeqNo - static_cast(ACK_WND_SIZE)) && ctrlpkt.getAckSeqNo() <= m_iAckSeqNo) { - LOGC(inlog.Note, - log << CONID() << "ACKACK out of order, skipping RTT calculation " - << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iSRTT << ")"); + string why; + if (frequentLogAllowed(FREQLOGFA_ACKACK_OUTOFORDER, tsArrival, (why))) + { + LOGC(inlog.Note, + log << CONID() << "ACKACK out of order, skipping RTT calculation " + << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo + << ", RTT (EWMA): " << m_iSRTT << ")." << why); + } +#if SRT_ENABLE_FREQUENT_LOG_TRACE + else + { + LOGC(qrlog.Note, log << "SUPPRESSED: ACKACK out of order LOG: " << why); + } +#endif + return; } diff --git a/srtcore/core.h b/srtcore/core.h index 3d5314281..ed250c641 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -948,13 +948,15 @@ class CUDT int32_t m_iReXmitCount; // Re-Transmit Count since last ACK static const size_t - MAX_FREQLOGFA = 2, FREQLOGFA_ENCRYPTION_FAILURE = 0, - FREQLOGFA_RCV_DROPPED = 1; - atomic_time_point m_tsLogSlowDown; // The last time a log message from the "slow down" group was shown. - // The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors). - // Currently only used by decryption failure message, therefore no mutex protection needed. - sync::atomic m_LogSlowDownExpired; // Can't use bitset because atomic + FREQLOGFA_RCV_DROPPED = 1, + FREQLOGFA_ACKACK_OUTOFORDER = 2, + MAX_FREQLOGFA = 3; + + atomic_time_point m_tsLogSlowDown[MAX_FREQLOGFA]; // The last time a log message from the "slow down" group was shown. + // The "slow down" group of logs are those that can be printed too often otherwise, but can't be turned off (warnings and errors). + // Currently only used by decryption failure message, therefore no mutex protection needed. + sync::atomic m_LogSlowDownExpired; // Can't use bitset because atomic sync::atomic m_aSuppressedMsg[MAX_FREQLOGFA]; /// @brief Check if a frequent log can be shown. From eecc176b3c11e4c0d87e78dc28f016b804055be8 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 20 Aug 2024 10:51:46 +0200 Subject: [PATCH 80/85] [core] Added busy counter for sockets and various fixes for data race problems (#2893). --- srtcore/api.cpp | 90 +++++++++++++++++++++++++++++++++--- srtcore/api.h | 63 +++++++++++++++++++++++-- srtcore/core.cpp | 25 ++++++---- srtcore/queue.cpp | 40 +++++++++++++++- test/test_fec_rebuilding.cpp | 2 +- 5 files changed, 197 insertions(+), 23 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e64c60f29..bb5dd64fe 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1914,11 +1914,28 @@ int srt::CUDTUnited::close(const SRTSOCKET u) return 0; } #endif - CUDTSocket* s = locateSocket(u); - if (!s) - throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); +#if ENABLE_HEAVY_LOGGING + // Wrapping the log into a destructor so that it + // is printed AFTER the destructor of SocketKeeper. + struct ScopedExitLog + { + const CUDTSocket* const ps; + ScopedExitLog(const CUDTSocket* p): ps(p){} + ~ScopedExitLog() + { + if (ps) // Could be not acquired by SocketKeeper, occasionally + { + HLOGC(smlog.Debug, log << "CUDTUnited::close/end: @" << ps->m_SocketID << " busy=" << ps->isStillBusy()); + } + } + }; +#endif + + SocketKeeper k(*this, u, ERH_THROW); + IF_HEAVY_LOGGING(ScopedExitLog slog(k.socket)); + HLOGC(smlog.Debug, log << "CUDTUnited::close/begin: @" << u << " busy=" << k.socket->isStillBusy()); - return close(s); + return close(k.socket); } #if ENABLE_BONDING @@ -2547,6 +2564,45 @@ srt::CUDTGroup* srt::CUDTUnited::acquireSocketsGroup(CUDTSocket* s) } #endif +srt::CUDTSocket* srt::CUDTUnited::locateAcquireSocket(SRTSOCKET u, ErrorHandling erh) +{ + ScopedLock cg(m_GlobControlLock); + + CUDTSocket* s = locateSocket_LOCKED(u); + if (!s) + { + if (erh == ERH_THROW) + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); + return NULL; + } + + s->apiAcquire(); + return s; +} + +bool srt::CUDTUnited::acquireSocket(CUDTSocket* s) +{ + // Note that before using this function you must be certain + // that the socket isn't broken already and it still has at least + // one more GC cycle to live. In other words, you must be certain + // that this pointer passed here isn't dangling and was obtained + // directly from m_Sockets, or even better, has been acquired + // by some other functionality already, which is only about to + // be released earlier than you need. + ScopedLock cg(m_GlobControlLock); + s->apiAcquire(); + // Keep the lock so that no one changes anything in the meantime. + // If the socket m_Status == SRTS_CLOSED (set by setClosed()), then + // this socket is no longer present in the m_Sockets container + if (s->m_Status >= SRTS_BROKEN) + { + s->apiRelease(); + return false; + } + + return true; +} + srt::CUDTSocket* srt::CUDTUnited::locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn) { ScopedLock cg(m_GlobControlLock); @@ -2613,7 +2669,7 @@ void srt::CUDTUnited::checkBrokenSockets() if (s->m_Status == SRTS_LISTENING) { - const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp; + const steady_clock::duration elapsed = steady_clock::now() - s->m_tsClosureTimeStamp.load(); // 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)) @@ -2672,6 +2728,20 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { CUDTSocket* ps = j->second; + + // NOTE: There is still a hypothetical risk here that ps + // was made busy while the socket was already moved to m_ClosedSocket, + // if the socket was acquired through CUDTUnited::acquireSocket (that is, + // busy flag acquisition was done through the CUDTSocket* pointer rather + // than through the numeric ID). Therefore this way of busy acquisition + // should be done only if at the moment of acquisition there are certainly + // other conditions applying on the socket that prevent it from being deleted. + if (ps->isStillBusy()) + { + HLOGC(smlog.Debug, log << "checkBrokenSockets: @" << ps->m_SocketID << " is still busy, SKIPPING THIS CYCLE."); + continue; + } + CUDT& u = ps->core(); // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); @@ -2691,7 +2761,7 @@ void srt::CUDTUnited::checkBrokenSockets() // timeout 1 second to destroy a socket AND it has been removed from // RcvUList const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp; + const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp.load(); if (closed_ago > seconds_from(1)) { CRNode* rnode = u.m_pRNode; @@ -2741,6 +2811,14 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) if (rn && rn->m_bOnList) return; + if (s->isStillBusy()) + { + HLOGC(smlog.Debug, log << "@" << s->m_SocketID << " is still busy, NOT deleting"); + return; + } + + LOGC(smlog.Note, log << "@" << s->m_SocketID << " busy=" << s->isStillBusy()); + #if ENABLE_BONDING if (s->m_GroupOf) { diff --git a/srtcore/api.h b/srtcore/api.h index 6dbad6634..b16784bbd 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,6 +123,18 @@ class CUDTSocket void construct(); +private: + srt::sync::atomic m_iBusy; +public: + void apiAcquire() { ++m_iBusy; } + void apiRelease() { --m_iBusy; } + + int isStillBusy() const + { + return m_iBusy; + } + + SRT_ATTR_GUARDED_BY(m_ControlLock) sync::atomic m_Status; //< current socket state @@ -131,7 +143,8 @@ 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()). - sync::steady_clock::time_point m_tsClosureTimeStamp; + //sync::steady_clock::time_point m_tsClosureTimeStamp; + sync::AtomicClock m_tsClosureTimeStamp; sockaddr_any m_SelfAddr; //< local address of the socket sockaddr_any m_PeerAddr; //< peer address of the socket @@ -324,7 +337,7 @@ class CUDTUnited int epoll_release(const int eid); #if ENABLE_BONDING - // [[using locked(m_GlobControlLock)]] + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_GlobControlLock) CUDTGroup& addGroup(SRTSOCKET id, SRT_GROUP_TYPE type) { // This only ensures that the element exists. @@ -346,7 +359,7 @@ class CUDTUnited void deleteGroup(CUDTGroup* g); void deleteGroup_LOCKED(CUDTGroup* g); - // [[using locked(m_GlobControlLock)]] + SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_GlobControlLock) CUDTGroup* findPeerGroup_LOCKED(SRTSOCKET peergroup) { for (groups_t::iterator i = m_Groups.begin(); i != m_Groups.end(); ++i) @@ -445,8 +458,50 @@ class CUDTUnited } } }; - #endif + + CUDTSocket* locateAcquireSocket(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); + bool acquireSocket(CUDTSocket* s); + +public: + struct SocketKeeper + { + CUDTSocket* socket; + + SocketKeeper(): socket(NULL) {} + + // This is intended for API functions to lock the socket's existence + // for the lifetime of their call. + SocketKeeper(CUDTUnited& glob, SRTSOCKET id, ErrorHandling erh = ERH_RETURN) { socket = glob.locateAcquireSocket(id, erh); } + + // This is intended for TSBPD thread that should lock the socket's + // existence until it exits. + SocketKeeper(CUDTUnited& glob, CUDTSocket* s) + { + acquire(glob, s); + } + + // Note: acquire doesn't check if the keeper already keeps anything. + // This is only for a use together with an empty constructor. + bool acquire(CUDTUnited& glob, CUDTSocket* s) + { + const bool caught = glob.acquireSocket(s); + socket = caught ? s : NULL; + return caught; + } + + ~SocketKeeper() + { + if (socket) + { + SRT_ASSERT(socket->isStillBusy() > 0); + socket->apiRelease(); + } + } + }; + +private: + void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index bae245392..eca2b2069 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5844,6 +5844,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } #if ENABLE_BONDING + m_ConnectionLock.unlock(); // The socket and the group are only linked to each other after interpretSrtHandshake(..) has been called. // Keep the group alive for the lifetime of this function, // and do it BEFORE acquiring m_ConnectionLock to avoid @@ -5851,6 +5852,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // This will check if a socket belongs to a group and if so // it will remember this group and keep it alive here. CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); + m_ConnectionLock.lock(); #endif if (!prepareBuffers(NULL)) @@ -8759,7 +8761,7 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr if (m_config.bDriftTracer) { #if ENABLE_BONDING - ScopedLock glock(uglobal().m_GlobControlLock); + ScopedLock glock(uglobal().m_GlobControlLock); // XXX not too excessive? const bool drift_updated = #endif m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); @@ -11737,17 +11739,20 @@ void srt::CUDT::completeBrokenConnectionDependencies(int errorcode) // Bound to one call because this requires locking pg->updateFailedLink(); } + // Sockets that never succeeded to connect must be deleted + // explicitly, otherwise they will never be deleted. OTOH + // the socket can be on the path of deletion already, so + // this only makes sure that the socket will be deleted, + // one way or another. + if (pending_broken) + { + // XXX This somehow can cause a deadlock + // uglobal()->close(m_parent); + LOGC(smlog.Debug, log << "updateBrokenConnection...: BROKEN SOCKET @" << m_SocketID << " - CLOSING, to be removed from group."); + m_parent->setBrokenClosed(); + } } - // Sockets that never succeeded to connect must be deleted - // explicitly, otherwise they will never be deleted. - if (pending_broken) - { - // XXX This somehow can cause a deadlock - // uglobal()->close(m_parent); - LOGC(smlog.Debug, log << "updateBrokenConnection...: BROKEN SOCKET @" << m_SocketID << " - CLOSING, to be removed from group."); - m_parent->setBrokenClosed(); - } #endif } diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 31a1460fd..6cb4faeb1 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -574,6 +574,13 @@ void* srt::CSndQueue::worker(void* param) continue; } + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), u->id()); + if (!sk.socket) + { + HLOGC(qslog.Debug, log << "Socket to be processed was deleted in the meantime, not packing"); + continue; + } + // pack a packet from the socket CPacket pkt; steady_clock::time_point next_send_time; @@ -929,6 +936,16 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), i->id); + if (!sk.socket) + { + // Socket deleted already, so stop this and proceed to the next loop. + LOGC(cnlog.Error, log << "updateConnStatus: IPE: socket @" << i->id << " already closed, proceed to only removal from lists"); + toRemove.push_back(*i); + continue; + } + + if (cst != CONN_RENDEZVOUS && dest_id != 0) { if (i->id != dest_id) @@ -974,14 +991,22 @@ 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); - // + remove(i->id); + + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), i->id); + if (!sk.socket) + { + // This actually shall never happen, so it's a kind of paranoid check. + LOGC(cnlog.Error, log << "updateConnStatus: IPE: socket @" << i->id << " already closed, NOT ACCESSING its contents"); + continue; + } + // 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); // 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 @@ -992,6 +1017,11 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst CUDT::uglobal().m_EPoll.update_events( i->u->m_SocketID, i->u->m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR, true); + // Make sure that the socket wasn't deleted in the meantime. + // Skip this part if it was. Note also that if the socket was + // decided to be deleted, it's already moved to m_ClosedSockets + // and should have been therefore already processed for deletion. + i->u->completeBrokenConnectionDependencies(i->errorcode); } @@ -1442,6 +1472,12 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CU HLOGC(cnlog.Debug, log << "worker_ProcessAddressedPacket: resending to QUEUED socket @" << id); return worker_TryAsyncRend_OrStore(id, unit, addr); } + // Although we donĀ“t have an exclusive passing here, + // we can count on that when the socket was once present in the hash, + // it will not be deleted for at least one GC cycle. But we still need + // to maintain the object existence until it's in use. + // Note that here we are out of any locks, so m_GlobControlLock can be locked. + CUDTUnited::SocketKeeper sk (CUDT::uglobal(), u->m_parent); // Found associated CUDT - process this as control or data packet // addressed to an associated socket. diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 185b54e98..ac57e5265 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -544,7 +544,7 @@ TEST(TestFEC, ConnectionMess) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); - ASSERT_NE(a, SRT_ERROR); + ASSERT_NE(a, SRT_ERROR) << srt_getlasterror_str(); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); // Now that the connection is established, check negotiated config From c10d55a084ec7bbb56b17de9da4b59562124dc2c Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Tue, 20 Aug 2024 20:06:58 +0200 Subject: [PATCH 81/85] [tests] Fixed unit tests for CSndRateEstimator. --- test/test_snd_rate_estimator.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/test_snd_rate_estimator.cpp b/test/test_snd_rate_estimator.cpp index e3d512424..c658c659c 100644 --- a/test/test_snd_rate_estimator.cpp +++ b/test/test_snd_rate_estimator.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "buffer_tools.h" #include "sync.h" +#include "packet.h" using namespace srt; using namespace std; @@ -53,6 +54,7 @@ TEST_F(CSndRateEstFixture, Empty) TEST_F(CSndRateEstFixture, CBRSending) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Generate CBR sending for 2.1 seconds to wrap the buffer around. for (int i = 0; i < 2100; ++i) { @@ -61,7 +63,7 @@ TEST_F(CSndRateEstFixture, CBRSending) const auto rate = m_rateEst.getRate(); if (i >= 100) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } @@ -72,6 +74,7 @@ TEST_F(CSndRateEstFixture, CBRSending) // only for one sampling period. TEST_F(CSndRateEstFixture, CBRSendingAfterPause) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Send 100 packets with 1000 bytes each for (int i = 0; i < 3100; ++i) { @@ -82,7 +85,7 @@ TEST_F(CSndRateEstFixture, CBRSendingAfterPause) const auto rate = m_rateEst.getRate(); if (i >= 100 && !(i >= 2000 && i < 2100)) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } @@ -92,6 +95,7 @@ TEST_F(CSndRateEstFixture, CBRSendingAfterPause) // Those empty samples should be included in bitrate estimation. TEST_F(CSndRateEstFixture, CBRSendingShortPause) { + const size_t hdrBytes = srt::CPacket::HDR_SIZE; // Send 100 packets with 1000 bytes each for (int i = 0; i < 3100; ++i) { @@ -102,9 +106,9 @@ TEST_F(CSndRateEstFixture, CBRSendingShortPause) const auto rate = m_rateEst.getRate(); if (i >= 1500 && i < 2000) - EXPECT_EQ(rate, 658000) << "i=" << i; + EXPECT_EQ(rate, 658000 + 500 * hdrBytes) << "i=" << i; else if (i >= 100) - EXPECT_EQ(rate, 1316000) << "i=" << i; + EXPECT_EQ(rate, 1316000 + 1000 * hdrBytes) << "i=" << i; else EXPECT_EQ(rate, 0) << "i=" << i; } From 2fff34ae9aaf50a7d0636b2bf5a1638d253c4d03 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Wed, 21 Aug 2024 13:10:16 +0200 Subject: [PATCH 82/85] [core] Fixed group lock-order-inversion (#3013). --- srtcore/api.h | 2 ++ srtcore/group.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index b16784bbd..b5d6be915 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -485,6 +485,8 @@ class CUDTUnited // This is only for a use together with an empty constructor. bool acquire(CUDTUnited& glob, CUDTSocket* s) { + if (s == NULL) + return false; const bool caught = glob.acquireSocket(s); socket = caught ? s : NULL; return caught; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index ee28029ae..5601cdeee 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -780,13 +780,18 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) bool is_set_on_socket = false; { - ScopedLock cg(m_GroupLock); + // Can't have m_GroupLock locked while calling getOpt on a member socket + // because the call will acquire m_ControlLock leading to a lock-order-inversion. + enterCS(m_GroupLock); gli_t gi = m_Group.begin(); - if (gi != m_Group.end()) + CUDTSocket* const ps = (gi != m_Group.end()) ? gi->ps : NULL; + CUDTUnited::SocketKeeper sk(CUDT::uglobal(), ps); + leaveCS(m_GroupLock); + if (sk.socket) { // Return the value from the first member socket, if any is present // Note: Will throw exception if the request is wrong. - gi->ps->core().getOpt(optname, (pw_optval), (w_optlen)); + sk.socket->core().getOpt(optname, (pw_optval), (w_optlen)); is_set_on_socket = true; } } From 6df4951f008ef7effdd26d5d484097f661deb090 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Fri, 23 Aug 2024 10:57:04 +0200 Subject: [PATCH 83/85] [docs] A minor fix of the release notes script readme. --- scripts/release-notes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-notes/README.md b/scripts/release-notes/README.md index d6c4adbfc..d34b07bfa 100644 --- a/scripts/release-notes/README.md +++ b/scripts/release-notes/README.md @@ -11,7 +11,7 @@ git log --pretty=format:"%h|%s|%an|%ae" v1.4.0...HEAD > commits.csv Use the produced `commits.csv` file as an input to the script: ```shell -python scripts/release-notes/generate-release-notes.py commits.csv +python scripts/release-notes/generate_release_notes.py commits.csv ``` The script produces `release-notes.md` as an output. From 307ff043d05e3f39cdc21b6c3982452adf224f04 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Aug 2024 14:37:37 +0200 Subject: [PATCH 84/85] [tests] Fixed unit tests compiler warnings AppleClang 15. --- test/test_bonding.cpp | 10 +++++----- test/test_buffer_rcv.cpp | 24 ++++++++++++------------ test/test_connection_timeout.cpp | 2 +- test/test_losslist_rcv.cpp | 18 +++++++++--------- test/test_socket_options.cpp | 4 ++-- test/test_sync.cpp | 10 +++++----- test/test_utilities.cpp | 4 ++-- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index c5e362e69..f40a52eb5 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -429,14 +429,14 @@ TEST(Bonding, Options) int revlat = -1; int optsize = sizeof revlat; EXPECT_NE(srt_getsockflag(grp, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(optsize, (int) sizeof revlat); EXPECT_EQ(revlat, 500); revlat = -1; optsize = sizeof revlat; // Expect the same value set on the member socket EXPECT_NE(srt_getsockflag(member, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(optsize, (int) sizeof revlat); EXPECT_EQ(revlat, 500); // Individual socket option modified on group @@ -451,7 +451,7 @@ TEST(Bonding, Options) // But getting the option value should be equal to the group setting EXPECT_NE(srt_getsockflag(grp, SRTO_OHEADBW, &ohead, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof ohead); + EXPECT_EQ(optsize, (int) sizeof ohead); EXPECT_EQ(ohead, 12); #if SRT_ENABLE_ENCRYPTION @@ -459,11 +459,11 @@ TEST(Bonding, Options) uint32_t kms = -1; EXPECT_NE(srt_getsockflag(grp, SRTO_KMSTATE, &kms, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(optsize, (int) sizeof kms); EXPECT_EQ(kms, int(SRT_KM_S_SECURED)); EXPECT_NE(srt_getsockflag(grp, SRTO_PBKEYLEN, &kms, &optsize), SRT_ERROR); - EXPECT_EQ(optsize, sizeof kms); + EXPECT_EQ(optsize, (int) sizeof kms); EXPECT_EQ(kms, 16); #ifdef ENABLE_AEAD_API_PREVIEW diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 554c6aae5..511c2dcb1 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -236,7 +236,7 @@ TEST_F(CRcvBufferReadMsg, OnePacketGap) { const size_t msg_bytelen = m_payload_sz; EXPECT_TRUE(rcv_buffer.isRcvDataReady()); - EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_EQ(readMessage(buff.data(), buff.size()), (int) 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()); @@ -317,7 +317,7 @@ TEST_F(CRcvBufferReadMsg, PacketDropByMsgNoSeqNo) // Let's say SND does not have the very first packet of the message, // therefore seqnolo of the msg drop request starts with the second packet of the message. - EXPECT_EQ(rcv_buffer.dropMessage(CSeqNo::incseq(m_init_seqno), CSeqNo::incseq(m_init_seqno, msg_len_pkts - 1), msgno, CRcvBuffer::KEEP_EXISTING), msg_len_pkts); + EXPECT_EQ(rcv_buffer.dropMessage(CSeqNo::incseq(m_init_seqno), CSeqNo::incseq(m_init_seqno, msg_len_pkts - 1), msgno, CRcvBuffer::KEEP_EXISTING), (int) msg_len_pkts); EXPECT_FALSE(hasAvailablePackets()); EXPECT_FALSE(rcv_buffer.isRcvDataReady()); @@ -343,7 +343,7 @@ TEST_F(CRcvBufferReadMsg, OnePacket) EXPECT_TRUE(hasAvailablePackets()); const int res2 = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res2, msg_bytelen); + EXPECT_EQ(res2, (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno)); EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } @@ -419,7 +419,7 @@ TEST_F(CRcvBufferReadMsg, MsgAcked) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -478,7 +478,7 @@ TEST_F(CRcvBufferReadMsg, MsgHalfAck) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -502,7 +502,7 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK) 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); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -529,7 +529,7 @@ TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap) 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); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -598,7 +598,7 @@ TEST_F(CRcvBufferReadMsg, LongMsgReadReady) EXPECT_TRUE(hasAvailablePackets()); const int res = readMessage(buff.data(), buff.size()); - EXPECT_EQ(res, msg_bytelen); + EXPECT_EQ(res, (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { const ptrdiff_t offset = i * m_payload_sz; @@ -621,7 +621,7 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) 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); + EXPECT_EQ(res, (int) 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 + int(i))); @@ -660,7 +660,7 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) 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); + EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), (int) msg_bytelen); for (size_t i = 0; i < msg_pkts; ++i) { EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, int(m_init_seqno + msg_i * msg_pkts + i))); @@ -706,7 +706,7 @@ TEST_F(CRcvBufferReadMsg, OnePacketTSBPD) // Read out the first message const int read_len = m_rcv_buffer->readMessage(buff.data(), buff.size()); - EXPECT_EQ(read_len, msg_bytelen); + EXPECT_EQ(read_len, (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), read_len, m_init_seqno)); // Check the state after a packet was read @@ -768,7 +768,7 @@ TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid) const size_t msg_bytelen = m_payload_sz; array buff; - EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen); + EXPECT_EQ(readMessage(buff.data(), buff.size()), (int) msg_bytelen); EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno)); EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index dca7595b8..ec52d9dd8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -262,7 +262,7 @@ TEST(TestConnectionAPI, Accept) { // Do extra checks only if you know that this was returned. EXPECT_EQ(ready[0].fd, caller_sock); - EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0); + EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0u); } srt_close(caller_sock); srt_close(listener_sock); diff --git a/test/test_losslist_rcv.cpp b/test/test_losslist_rcv.cpp index 9ab4f382b..94fea6ceb 100644 --- a/test/test_losslist_rcv.cpp +++ b/test/test_losslist_rcv.cpp @@ -81,7 +81,7 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) CRcvFreshLoss (45, 80, 100) }; - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Ok, now let's do element removal @@ -90,7 +90,7 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // Now we expect to have [10-15] [25-25] [27-35]... // After revoking 25 it should have removed it. @@ -99,41 +99,41 @@ TEST(CRcvFreshLossListTest, CheckFreshLossList) rm = CRcvFreshLoss::removeOne((floss), 27, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // STRIP rm = CRcvFreshLoss::removeOne((floss), 28, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // DELETE rm = CRcvFreshLoss::removeOne((floss), 25, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 10); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // SPLIT rm = CRcvFreshLoss::removeOne((floss), 50, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 100); - EXPECT_EQ(floss.size(), 5); + EXPECT_EQ(floss.size(), 5u); // DELETE rm = CRcvFreshLoss::removeOne((floss), 30, &had_ttl); EXPECT_EQ(rm, true); EXPECT_EQ(had_ttl, 3); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Remove nonexistent sequence, but existing before. rm = CRcvFreshLoss::removeOne((floss), 25, NULL); EXPECT_EQ(rm, false); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); // Remove nonexistent sequence that didn't exist before. rm = CRcvFreshLoss::removeOne((floss), 31, &had_ttl); EXPECT_EQ(rm, false); EXPECT_EQ(had_ttl, 0); - EXPECT_EQ(floss.size(), 4); + EXPECT_EQ(floss.size(), 4u); } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 271d84186..a4c90af37 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -245,7 +245,7 @@ void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueTy << "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"; + EXPECT_EQ(opt_len, (int) entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; } typedef char const* strptr; @@ -258,7 +258,7 @@ void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); EXPECT_EQ(strncmp(opt_val, value, min(opt_len, (int)entry.opt_len)), 0) << desc << ": Wrong " << entry.optname << " value " << opt_val; - EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; + EXPECT_EQ(opt_len, (int) entry.opt_len) << desc << "Wrong " << entry.optname << " value length"; } template diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 9e2064cc1..d06b5e5f5 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -159,7 +159,7 @@ TEST(SyncRandom, GenRandomInt) { const int rand_val = genRandomInt(0, int(mn.size()) - 1); ASSERT_GE(rand_val, 0); - ASSERT_LT(rand_val, mn.size()); + ASSERT_LT(rand_val, (int) mn.size()); ++mn[rand_val]; } @@ -445,7 +445,7 @@ TEST(SyncEvent, WaitForTwoNotifyOne) using future_t = decltype(async(launch::async, wait_async, &cond, &mutex, timeout, 0)); - future_t future_result[2] = { + std::array future_result = { async(launch::async, wait_async, &cond, &mutex, timeout, 0), async(launch::async, wait_async, &cond, &mutex, timeout, 1) }; @@ -462,9 +462,9 @@ 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(500))), - move(future_result[1].wait_for(chrono::microseconds(500))) + std::array wait_state = { + future_result[0].wait_for(chrono::microseconds(500)), + future_result[1].wait_for(chrono::microseconds(500)) }; cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size() diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp index 1d2c1aa59..28c14044e 100644 --- a/test/test_utilities.cpp +++ b/test/test_utilities.cpp @@ -160,11 +160,11 @@ TEST(CircularBuffer, Overall) ASSERT_EQ(output.d, 22.1); IF_HEAVY_LOGGING(cerr << "Pushing 1 aslong there is capacity:\n"); - int i = 0; + IF_HEAVY_LOGGING(int i = 0); while (buf.push(1) != -1) { IF_HEAVY_LOGGING(cerr << "Pushed, begin=" << buf.m_xBegin << " end=" << buf.m_xEnd << endl); - ++i; + IF_HEAVY_LOGGING(++i); } IF_HEAVY_LOGGING(cerr << "Done " << i << " operations, buffer:\n"); IF_HEAVY_LOGGING(ShowCircularBuffer(buf)); From 9c7206f0190c0c800a5ee1e71ee61ec0d4c7e216 Mon Sep 17 00:00:00 2001 From: Maxim Sharabayko Date: Thu, 22 Aug 2024 18:14:31 +0200 Subject: [PATCH 85/85] [apps] Fixed compiler warnings (G++ 9.4) --- testing/srt-test-mpbond.cpp | 2 +- testing/testmedia.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index bf9739675..60d91df2c 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -209,7 +209,6 @@ int main( int argc, char** argv ) return 1; } - auto s = new SrtSource; unique_ptr src; unique_ptr tar; @@ -222,6 +221,7 @@ int main( int argc, char** argv ) Verb() << "SRT -> " << outspec; tar = Target::Create(outspec); + auto s = new SrtSource; s->Acquire(conngrp); src.reset(s); } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index d82212bad..b9d8a0413 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1059,8 +1059,8 @@ void SrtCommon::OpenGroupClient() if (!extras.empty()) { Verb() << "?" << extras[0] << VerbNoEOL; - for (size_t i = 1; i < extras.size(); ++i) - Verb() << "&" << extras[i] << VerbNoEOL; + for (size_t ii = 1; ii < extras.size(); ++ii) + Verb() << "&" << extras[ii] << VerbNoEOL; } Verb(); @@ -1194,11 +1194,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int ii = 0; ii < len1; ++ii) + Verb() << " " << ready_conn[ii] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int ii = 0; ii < len2; ++ii) + Verb() << " " << ready_err[ii] << VerbNoEOL; Verb() << "";