From 37aa8032e1efb094ef0aee32946568b6e6538d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 9 May 2018 13:33:12 +0200 Subject: [PATCH 001/517] Added check code for attempting rendezvous to self --- srtcore/api.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++ srtcore/netinet_any.h | 19 ++++++++++ 2 files changed, 103 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 2d4dcdc72..4c70cc74c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -65,6 +65,9 @@ modified by #ifdef WIN32 #include + #include // getting local interfaces +#else + #include // getting local interfaces #endif using namespace std; @@ -824,6 +827,87 @@ int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, in else if (s->m_Status != SRTS_OPENED) throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); + if (s->m_pUDT->m_bRendezvous) + { + sockaddr_any bound = s->m_pSelfAddr; + sockaddr_any target = name; + + if (!bound.isany()) + { + // Bound to a specific local address, so only check if + // this isn't the same address as 'target'. + if (target.equal_address(bound)) + { + LOGC(mglog.Error, log << "connect: Rendezvous attempt to self on addr: " + << SockaddrToString(&target.sa) << " - rejected"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + } + } + else + { + // Bound to INADDR_ANY, so matching with any local IP address is invalid. + vector locals; +#ifdef WIN32 + ULONG family = s->m_pSeflAddr->sa_family; + ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; + ULONG outBufLen = 0; + + // This function doesn't allocate memory by itself, you have to do it + // yourself, worst case when it's too small, the size will be corrected + // and the function will do nothing. So, simply, call the function with + // always too little 0 size and make it show the correct one. + GetAdaptersAddresses(family, flags, NULL, NULL, &outBufLen); + // Ignore errors. Check errors on the real call. + + // Good, now we can allocate memory + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)::operator new(outBufLen); + ULONG st = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + if (st == ERROR_SUCCESS) + { + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; + while (pUnicast) + { + locals.push_back(pUnicast->Address.lpSockaddr); + pUnicast = pUnicast->Next; + } + } + + ::operator delete(pAddresses); + +#else + // Use POSIX method: getifaddrs + struct ifaddrs* pif, * pifa; + int st = getifaddrs(&pifa); + if (st == 0) + { + for (pif = pifa; pif; pif = pif->ifa_next) + { + locals.push_back(pif->ifa_addr); + } + } + + freeifaddrs(pifa); +#endif + + // If any of the above function fails, it will collect + // no local interfaces, so it's impossible to check anything. + // OTOH it should also mean that the network isn't working, + // so it's unlikely, as well as no address should match the + // local address anyway. + for (size_t i = 0; i < locals.size(); ++i) + { + if (locals[i].equal_address(target)) + { + LOGC(mglog.Error, log << "connect: Rendezvous bound to any, but target (" + << SockaddrToString(&target.sa) << ") matches one of local interfaces"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + } + } + // connect_complete() may be called before connect() returns. // So we need to update the status before connect() is called, // otherwise the status may be overwritten with wrong value diff --git a/srtcore/netinet_any.h b/srtcore/netinet_any.h index 0418a1a84..ec6435a4c 100644 --- a/srtcore/netinet_any.h +++ b/srtcore/netinet_any.h @@ -42,6 +42,17 @@ struct sockaddr_any len = size(); } + sockaddr_any(const sockaddr* src) + { + // It's not safe to copy it directly, so check. + if (src->sa_family == AF_INET) + memcpy(&sin, src, sizeof sin); + + // Note: this isn't too safe, may crash for stupid values + // in the source structure, so make sure it's correct first. + memcpy(&sin6, src, sizeof sin6); + } + socklen_t size() const { switch (sa.sa_family) @@ -120,6 +131,14 @@ struct sockaddr_any return memcmp(&c1, &c2, sizeof(c1)) < 0; } }; + + // Tests if the current address is the "any" wildcard. + bool isany() + { + if (sa.sa_family == AF_INET) + return sin.sin_addr.s_addr == INADDR_ANY; + return 0 == memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any); + } }; template<> struct sockaddr_any::TypeMap { typedef sockaddr_in type; }; From 7aaeeb34f98e16a6f153034e531cd1dbe97a9a2c Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 9 May 2018 16:05:16 +0200 Subject: [PATCH 002/517] Fixed build breaks on MICROSOFT --- CMakeLists.txt | 21 ++++++++++----------- srtcore/api.cpp | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56b9f2548..a804fdb3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,16 +434,20 @@ endif() set (VIRTUAL_srt $ $) set (HEADERS_srt ${HEADERS_srt} ${HEADERS_srt_win32}) +macro(srt_update_library_deps target) + list (APPEND INSTALL_TARGETS ${target}) + target_link_libraries(${target} PRIVATE ${SSL_LIBRARIES}) + if (MICROSOFT) + target_link_libraries(${target} PRIVATE ws2_32.lib Iphlpapi.lib) + endif() +endmacro() + if (srt_libspec_shared) add_library(${TARGET_srt}_shared SHARED ${VIRTUAL_srt}) # shared libraries need PIC set_property(TARGET ${TARGET_srt}_shared PROPERTY OUTPUT_NAME ${TARGET_srt}) set_target_properties (${TARGET_srt}_shared PROPERTIES VERSION ${SRT_VERSION} SOVERSION ${SRT_VERSION_MAJOR}) - list (APPEND INSTALL_TARGETS ${TARGET_srt}_shared) - target_link_libraries(${TARGET_srt}_shared PRIVATE ${SSL_LIBRARIES}) - if (MICROSOFT) - target_link_libraries(${TARGET_srt}_shared PRIVATE ws2_32.lib) - endif() + srt_update_library_deps(${TARGET_srt}_shared) endif() if (srt_libspec_static) @@ -462,12 +466,7 @@ if (srt_libspec_static) else() set_property(TARGET ${TARGET_srt}_static PROPERTY OUTPUT_NAME ${TARGET_srt}) endif() - - list (APPEND INSTALL_TARGETS ${TARGET_srt}_static) - target_link_libraries(${TARGET_srt}_static PRIVATE ${SSL_LIBRARIES}) - if (MICROSOFT) - target_link_libraries(${TARGET_srt}_static PRIVATE ws2_32.lib) - endif() + srt_update_library_deps(${TARGET_srt}_static) endif() diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4c70cc74c..3d938ab4b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -849,7 +849,7 @@ int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, in // Bound to INADDR_ANY, so matching with any local IP address is invalid. vector locals; #ifdef WIN32 - ULONG family = s->m_pSeflAddr->sa_family; + ULONG family = s->m_pSelfAddr->sa_family; ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; ULONG outBufLen = 0; From e39d94da11e67305593dd401b5a915555da5ba32 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 16 May 2018 11:33:06 +0200 Subject: [PATCH 003/517] Fixed copying by type recognition condition --- srtcore/netinet_any.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/srtcore/netinet_any.h b/srtcore/netinet_any.h index ec6435a4c..648c0b022 100644 --- a/srtcore/netinet_any.h +++ b/srtcore/netinet_any.h @@ -46,11 +46,16 @@ struct sockaddr_any { // It's not safe to copy it directly, so check. if (src->sa_family == AF_INET) + { memcpy(&sin, src, sizeof sin); - - // Note: this isn't too safe, may crash for stupid values - // in the source structure, so make sure it's correct first. - memcpy(&sin6, src, sizeof sin6); + } + else // assume AF_INET6 + { + // Note: this isn't too safe, may crash for stupid values + // of src->sa_family or any other data + // in the source structure, so make sure it's correct first. + memcpy(&sin6, src, sizeof sin6); + } } socklen_t size() const From d798b01a7cc27c641b9acd229a4e5820ddcab0d5 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 16 May 2018 11:38:17 +0200 Subject: [PATCH 004/517] Changed style --- srtcore/netinet_any.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/netinet_any.h b/srtcore/netinet_any.h index 648c0b022..1b1e96be5 100644 --- a/srtcore/netinet_any.h +++ b/srtcore/netinet_any.h @@ -142,7 +142,7 @@ struct sockaddr_any { if (sa.sa_family == AF_INET) return sin.sin_addr.s_addr == INADDR_ANY; - return 0 == memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any); + return memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any) == 0; } }; From 2f5a50548f0f58e20ce7eb5055b74c81dd23a439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 17 May 2018 12:28:40 +0200 Subject: [PATCH 005/517] Split out apputil.cpp file --- apps/apputil.cpp | 185 +++++++++++++++++++++++++++++++++++++ apps/apputil.hpp | 185 ++----------------------------------- apps/support.maf | 1 + testing/srt-test-file.maf | 1 + testing/srt-test-live.maf | 1 + testing/srt-test-relay.maf | 1 + 6 files changed, 198 insertions(+), 176 deletions(-) create mode 100644 apps/apputil.cpp diff --git a/apps/apputil.cpp b/apps/apputil.cpp new file mode 100644 index 000000000..71e234606 --- /dev/null +++ b/apps/apputil.cpp @@ -0,0 +1,185 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +// For Options +#include +#include + +#include "apputil.hpp" + +// NOTE: MINGW currently does not include support for inet_pton(). See +// http://mingw.5.n7.nabble.com/Win32API-request-for-new-functions-td22029.html +// Even if it did support inet_pton(), it is only available on Windows Vista +// and later. Since we need to support WindowsXP and later in ORTHRUS. Many +// customers still use it, we will need to implement using something like +// WSAStringToAddress() which is available on Windows95 and later. +// Support for IPv6 was added on WindowsXP SP1. +// Header: winsock2.h +// Implementation: ws2_32.dll +// See: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742214(v=vs.85).aspx +// http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html +#ifdef __MINGW32__ +static int inet_pton(int af, const char * src, void * dst) +{ + struct sockaddr_storage ss; + int ssSize = sizeof(ss); + char srcCopy[INET6_ADDRSTRLEN + 1]; + + ZeroMemory(&ss, sizeof(ss)); + + // work around non-const API + strncpy(srcCopy, src, INET6_ADDRSTRLEN + 1); + srcCopy[INET6_ADDRSTRLEN] = '\0'; + + if (WSAStringToAddress( + srcCopy, af, NULL, (struct sockaddr *)&ss, &ssSize) != 0) + { + return 0; + } + + switch (af) + { + case AF_INET : + { + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + } + case AF_INET6 : + { + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + default : + { + // No-Op + } + } + + return 0; +} +#endif // __MINGW__ + +sockaddr_in CreateAddrInet(const std::string& name, unsigned short port) +{ + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + + if ( name != "" ) + { + if ( inet_pton(AF_INET, name.c_str(), &sa.sin_addr) == 1 ) + return sa; + + // XXX RACY!!! Use getaddrinfo() instead. Check portability. + // Windows/Linux declare it. + // See: + // http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html + hostent* he = gethostbyname(name.c_str()); + if ( !he || he->h_addrtype != AF_INET ) + throw std::invalid_argument("SrtSource: host not found: " + name); + + sa.sin_addr = *(in_addr*)he->h_addr_list[0]; + } + + return sa; +} + +std::string Join(const std::vector& in, std::string sep) +{ + if ( in.empty() ) + return ""; + + std::ostringstream os; + + os << in[0]; + for (auto i = in.begin()+1; i != in.end(); ++i) + os << sep << *i; + return os.str(); +} + + +options_t ProcessOptions(char* const* argv, int argc, std::vector scheme) +{ + using namespace std; + + string current_key = ""; + size_t vals = 0; + OptionScheme::Args type = OptionScheme::ARG_VAR; // This is for no-option-yet or consumed + map> params; + bool moreoptions = true; + + for (char* const* p = argv+1; p != argv+argc; ++p) + { + const char* a = *p; + // cout << "*D ARG: '" << a << "'\n"; + if ( moreoptions && a[0] == '-' ) + { + current_key = a+1; + if ( current_key == "-" ) + { + // The -- argument terminates the options. + // The default key is restored to empty so that + // it collects now all arguments under the empty key + // (not-option-assigned argument). + moreoptions = false; + goto EndOfArgs; + } + params[current_key].clear(); + vals = 0; + + // Find the key in the scheme. If not found, treat it as ARG_NONE. + for (auto s: scheme) + { + if (s.names.count(current_key)) + { + // cout << "*D found '" << current_key << "' in scheme type=" << int(s.type) << endl; + if ( s.type == OptionScheme::ARG_NONE ) + { + // Anyway, consider it already processed. + break; + } + type = s.type; + goto Found; + } + + } + // Not found: set ARG_NONE. + // cout << "*D KEY '" << current_key << "' assumed type NONE\n"; +EndOfArgs: + type = OptionScheme::ARG_VAR; + current_key = ""; +Found: + continue; + } + + // Collected a value - check if full + // cout << "*D COLLECTING '" << a << "' for key '" << current_key << "' (" << vals << " so far)\n"; + params[current_key].push_back(a); + ++vals; + if ( vals == 1 && type == OptionScheme::ARG_ONE ) + { + // cout << "*D KEY TYPE ONE - resetting to empty key\n"; + // Reset the key to "default one". + current_key = ""; + vals = 0; + type = OptionScheme::ARG_VAR; + } + else + { + // cout << "*D KEY type VAR - still collecting until the end of options or next option.\n"; + } + } + + return params; +} + diff --git a/apps/apputil.hpp b/apps/apputil.hpp index acded0b5a..be2549643 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -11,7 +11,12 @@ #ifndef INC__APPCOMMON_H #define INC__APPCOMMON_H - + +#include +#include +#include +#include + #if WIN32 // Keep this below commented out. @@ -55,112 +60,14 @@ inline void SysCleanupNetwork() {} #endif -#include -#include -// For Options -#include -#include -#include -#include -#include - -// NOTE: MINGW currently does not include support for inet_pton(). See -// http://mingw.5.n7.nabble.com/Win32API-request-for-new-functions-td22029.html -// Even if it did support inet_pton(), it is only available on Windows Vista -// and later. Since we need to support WindowsXP and later in ORTHRUS. Many -// customers still use it, we will need to implement using something like -// WSAStringToAddress() which is available on Windows95 and later. -// Support for IPv6 was added on WindowsXP SP1. -// Header: winsock2.h -// Implementation: ws2_32.dll -// See: -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742214(v=vs.85).aspx -// http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html -#ifdef __MINGW32__ -static inline int inet_pton(int af, const char * src, void * dst) -{ - struct sockaddr_storage ss; - int ssSize = sizeof(ss); - char srcCopy[INET6_ADDRSTRLEN + 1]; - - ZeroMemory(&ss, sizeof(ss)); - - // work around non-const API - strncpy(srcCopy, src, INET6_ADDRSTRLEN + 1); - srcCopy[INET6_ADDRSTRLEN] = '\0'; - - if (WSAStringToAddress( - srcCopy, af, NULL, (struct sockaddr *)&ss, &ssSize) != 0) - { - return 0; - } - - switch (af) - { - case AF_INET : - { - *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; - return 1; - } - case AF_INET6 : - { - *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; - return 1; - } - default : - { - // No-Op - } - } - - return 0; -} -#endif // __MINGW__ - #ifdef WIN32 inline int SysError() { return ::GetLastError(); } #else inline int SysError() { return errno; } #endif -inline sockaddr_in CreateAddrInet(const std::string& name, unsigned short port) -{ - sockaddr_in sa; - memset(&sa, 0, sizeof sa); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - - if ( name != "" ) - { - if ( inet_pton(AF_INET, name.c_str(), &sa.sin_addr) == 1 ) - return sa; - - // XXX RACY!!! Use getaddrinfo() instead. Check portability. - // Windows/Linux declare it. - // See: - // http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html - hostent* he = gethostbyname(name.c_str()); - if ( !he || he->h_addrtype != AF_INET ) - throw std::invalid_argument("SrtSource: host not found: " + name); - - sa.sin_addr = *(in_addr*)he->h_addr_list[0]; - } - - return sa; -} - -inline std::string Join(const std::vector& in, std::string sep) -{ - if ( in.empty() ) - return ""; - - std::ostringstream os; - - os << in[0]; - for (auto i = in.begin()+1; i != in.end(); ++i) - os << sep << *i; - return os.str(); -} +sockaddr_in CreateAddrInet(const std::string& name, unsigned short port); +std::string Join(const std::vector& in, std::string sep); typedef std::map> options_t; @@ -176,7 +83,6 @@ struct OutString static type process(const options_t::mapped_type& i) { return Join(i, " "); } }; - template inline typename OutType::type Option(const options_t&, OutValue deflt=OutValue()) { return deflt; } @@ -207,80 +113,7 @@ struct OptionScheme enum Args { ARG_NONE, ARG_ONE, ARG_VAR } type; }; -inline options_t ProcessOptions(char* const* argv, int argc, std::vector scheme) -{ - using namespace std; - - string current_key = ""; - size_t vals = 0; - OptionScheme::Args type = OptionScheme::ARG_VAR; // This is for no-option-yet or consumed - map> params; - bool moreoptions = true; - - for (char* const* p = argv+1; p != argv+argc; ++p) - { - const char* a = *p; - // cout << "*D ARG: '" << a << "'\n"; - if ( moreoptions && a[0] == '-' ) - { - current_key = a+1; - if ( current_key == "-" ) - { - // The -- argument terminates the options. - // The default key is restored to empty so that - // it collects now all arguments under the empty key - // (not-option-assigned argument). - moreoptions = false; - goto EndOfArgs; - } - params[current_key].clear(); - vals = 0; - - // Find the key in the scheme. If not found, treat it as ARG_NONE. - for (auto s: scheme) - { - if (s.names.count(current_key)) - { - // cout << "*D found '" << current_key << "' in scheme type=" << int(s.type) << endl; - if ( s.type == OptionScheme::ARG_NONE ) - { - // Anyway, consider it already processed. - break; - } - type = s.type; - goto Found; - } - - } - // Not found: set ARG_NONE. - // cout << "*D KEY '" << current_key << "' assumed type NONE\n"; -EndOfArgs: - type = OptionScheme::ARG_VAR; - current_key = ""; -Found: - continue; - } - - // Collected a value - check if full - // cout << "*D COLLECTING '" << a << "' for key '" << current_key << "' (" << vals << " so far)\n"; - params[current_key].push_back(a); - ++vals; - if ( vals == 1 && type == OptionScheme::ARG_ONE ) - { - // cout << "*D KEY TYPE ONE - resetting to empty key\n"; - // Reset the key to "default one". - current_key = ""; - vals = 0; - type = OptionScheme::ARG_VAR; - } - else - { - // cout << "*D KEY type VAR - still collecting until the end of options or next option.\n"; - } - } - - return params; -} +options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); #endif // INC__APPCOMMON_H diff --git a/apps/support.maf b/apps/support.maf index e0fc9eef9..5c1d6dfcc 100644 --- a/apps/support.maf +++ b/apps/support.maf @@ -8,6 +8,7 @@ # take selectively whichever parts they need. SOURCES +apputil.cpp logsupport.cpp socketoptions.cpp transmitmedia.cpp diff --git a/testing/srt-test-file.maf b/testing/srt-test-file.maf index 3e5e3c076..36a8582b7 100644 --- a/testing/srt-test-file.maf +++ b/testing/srt-test-file.maf @@ -2,6 +2,7 @@ SOURCES srt-test-file.cpp testmedia.cpp +../apps/apputil.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp ../apps/uriparser.cpp diff --git a/testing/srt-test-live.maf b/testing/srt-test-live.maf index f1c3341f0..f609b77b7 100644 --- a/testing/srt-test-live.maf +++ b/testing/srt-test-live.maf @@ -3,6 +3,7 @@ SOURCES srt-test-live.cpp testmedia.cpp +../apps/apputil.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp ../apps/uriparser.cpp diff --git a/testing/srt-test-relay.maf b/testing/srt-test-relay.maf index 07b4e1ef4..594a650af 100644 --- a/testing/srt-test-relay.maf +++ b/testing/srt-test-relay.maf @@ -3,6 +3,7 @@ SOURCES srt-test-relay.cpp testmedia.cpp +../apps/apputil.cpp ../apps/verbose.cpp ../apps/socketoptions.cpp ../apps/uriparser.cpp From c9200e131cb73630bc6ba713446eb2c1f0cf2d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 17 May 2018 12:59:18 +0200 Subject: [PATCH 006/517] Added prevention of self-connecting to testing applications --- CMakeLists.txt | 6 ++- apps/apputil.cpp | 103 ++++++++++++++++++++++++++++++++++++++++-- apps/apputil.hpp | 1 + srtcore/api.cpp | 84 ---------------------------------- testing/testmedia.cpp | 16 +++++++ 5 files changed, 120 insertions(+), 90 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a804fdb3a..0ebbb5b89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,7 +438,7 @@ macro(srt_update_library_deps target) list (APPEND INSTALL_TARGETS ${target}) target_link_libraries(${target} PRIVATE ${SSL_LIBRARIES}) if (MICROSOFT) - target_link_libraries(${target} PRIVATE ws2_32.lib Iphlpapi.lib) + target_link_libraries(${target} PRIVATE ws2_32.lib) endif() endmacro() @@ -642,6 +642,10 @@ macro(srt_make_application name) set_target_properties(${name} PROPERTIES COMPILE_FLAGS "${CFLAGS_CXX_STANDARD} ${EXTRA_stransmit}" ${FORCE_RPATH}) target_link_libraries(${name} ${srt_link_library}) + + if (MICROSOFT) + target_link_libraries(${target} PRIVATE Iphlpapi.lib) + endif() endmacro() macro(srt_add_application name sources) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 71e234606..b9823fee4 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -14,6 +14,16 @@ #include #include "apputil.hpp" +#include "netinet_any.h" + +#ifdef WIN32 + #include // getting local interfaces +#else + #include // getting local interfaces +#endif + +using namespace std; + // NOTE: MINGW currently does not include support for inet_pton(). See // http://mingw.5.n7.nabble.com/Win32API-request-for-new-functions-td22029.html @@ -68,7 +78,7 @@ static int inet_pton(int af, const char * src, void * dst) } #endif // __MINGW__ -sockaddr_in CreateAddrInet(const std::string& name, unsigned short port) +sockaddr_in CreateAddrInet(const string& name, unsigned short port) { sockaddr_in sa; memset(&sa, 0, sizeof sa); @@ -86,7 +96,7 @@ sockaddr_in CreateAddrInet(const std::string& name, unsigned short port) // http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html hostent* he = gethostbyname(name.c_str()); if ( !he || he->h_addrtype != AF_INET ) - throw std::invalid_argument("SrtSource: host not found: " + name); + throw invalid_argument("SrtSource: host not found: " + name); sa.sin_addr = *(in_addr*)he->h_addr_list[0]; } @@ -94,12 +104,12 @@ sockaddr_in CreateAddrInet(const std::string& name, unsigned short port) return sa; } -std::string Join(const std::vector& in, std::string sep) +string Join(const vector& in, string sep) { if ( in.empty() ) return ""; - std::ostringstream os; + ostringstream os; os << in[0]; for (auto i = in.begin()+1; i != in.end(); ++i) @@ -108,7 +118,7 @@ std::string Join(const std::vector& in, std::string sep) } -options_t ProcessOptions(char* const* argv, int argc, std::vector scheme) +options_t ProcessOptions(char* const* argv, int argc, vector scheme) { using namespace std; @@ -183,3 +193,86 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector return params; } +static vector GetLocalInterfaces() +{ + vector locals; +#ifdef WIN32 + ULONG family = s->m_pSelfAddr->sa_family; + ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; + ULONG outBufLen = 0; + + // This function doesn't allocate memory by itself, you have to do it + // yourself, worst case when it's too small, the size will be corrected + // and the function will do nothing. So, simply, call the function with + // always too little 0 size and make it show the correct one. + GetAdaptersAddresses(family, flags, NULL, NULL, &outBufLen); + // Ignore errors. Check errors on the real call. + + // Good, now we can allocate memory + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)::operator new(outBufLen); + ULONG st = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + if (st == ERROR_SUCCESS) + { + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; + while (pUnicast) + { + locals.push_back(pUnicast->Address.lpSockaddr); + pUnicast = pUnicast->Next; + } + } + + ::operator delete(pAddresses); + +#else + // Use POSIX method: getifaddrs + struct ifaddrs* pif, * pifa; + int st = getifaddrs(&pifa); + if (st == 0) + { + for (pif = pifa; pif; pif = pif->ifa_next) + { + locals.push_back(pif->ifa_addr); + } + } + + freeifaddrs(pifa); +#endif + return locals; +} + +bool IsTargetAddrSelf(const sockaddr* boundaddr, const sockaddr* targetaddr) +{ + sockaddr_any bound = boundaddr; + sockaddr_any target = targetaddr; + + if (!bound.isany()) + { + // Bound to a specific local address, so only check if + // this isn't the same address as 'target'. + if (target.equal_address(bound)) + { + return true; + } + } + else + { + // Bound to INADDR_ANY, so check matching with any local IP address + vector locals = GetLocalInterfaces(); + + // If any of the above function fails, it will collect + // no local interfaces, so it's impossible to check anything. + // OTOH it should also mean that the network isn't working, + // so it's unlikely, as well as no address should match the + // local address anyway. + for (size_t i = 0; i < locals.size(); ++i) + { + if (locals[i].equal_address(target)) + { + return true; + } + } + } + + return false; +} + diff --git a/apps/apputil.hpp b/apps/apputil.hpp index be2549643..746c7f71c 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -115,5 +115,6 @@ struct OptionScheme options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); +bool IsTargetAddrSelf(const sockaddr* boundaddr, const sockaddr* targetaddr); #endif // INC__APPCOMMON_H diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 3d938ab4b..2d4dcdc72 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -65,9 +65,6 @@ modified by #ifdef WIN32 #include - #include // getting local interfaces -#else - #include // getting local interfaces #endif using namespace std; @@ -827,87 +824,6 @@ int CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, in else if (s->m_Status != SRTS_OPENED) throw CUDTException(MJ_NOTSUP, MN_ISCONNECTED, 0); - if (s->m_pUDT->m_bRendezvous) - { - sockaddr_any bound = s->m_pSelfAddr; - sockaddr_any target = name; - - if (!bound.isany()) - { - // Bound to a specific local address, so only check if - // this isn't the same address as 'target'. - if (target.equal_address(bound)) - { - LOGC(mglog.Error, log << "connect: Rendezvous attempt to self on addr: " - << SockaddrToString(&target.sa) << " - rejected"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - } - } - else - { - // Bound to INADDR_ANY, so matching with any local IP address is invalid. - vector locals; -#ifdef WIN32 - ULONG family = s->m_pSelfAddr->sa_family; - ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; - ULONG outBufLen = 0; - - // This function doesn't allocate memory by itself, you have to do it - // yourself, worst case when it's too small, the size will be corrected - // and the function will do nothing. So, simply, call the function with - // always too little 0 size and make it show the correct one. - GetAdaptersAddresses(family, flags, NULL, NULL, &outBufLen); - // Ignore errors. Check errors on the real call. - - // Good, now we can allocate memory - PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)::operator new(outBufLen); - ULONG st = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); - if (st == ERROR_SUCCESS) - { - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; - while (pUnicast) - { - locals.push_back(pUnicast->Address.lpSockaddr); - pUnicast = pUnicast->Next; - } - } - - ::operator delete(pAddresses); - -#else - // Use POSIX method: getifaddrs - struct ifaddrs* pif, * pifa; - int st = getifaddrs(&pifa); - if (st == 0) - { - for (pif = pifa; pif; pif = pif->ifa_next) - { - locals.push_back(pif->ifa_addr); - } - } - - freeifaddrs(pifa); -#endif - - // If any of the above function fails, it will collect - // no local interfaces, so it's impossible to check anything. - // OTOH it should also mean that the network isn't working, - // so it's unlikely, as well as no address should match the - // local address anyway. - for (size_t i = 0; i < locals.size(); ++i) - { - if (locals[i].equal_address(target)) - { - LOGC(mglog.Error, log << "connect: Rendezvous bound to any, but target (" - << SockaddrToString(&target.sa) << ") matches one of local interfaces"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - } - } - // connect_complete() may be called before connect() returns. // So we need to update the status before connect() is called, // otherwise the status may be overwritten with wrong value diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 435e628a8..44b3122d0 100644 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -24,6 +24,7 @@ #endif #include "netinet_any.h" +#include "api.h" // SockaddrToString - XXX move to utils or some more suitable place #include "apputil.hpp" #include "socketoptions.hpp" #include "uriparser.hpp" @@ -579,6 +580,21 @@ void SrtCommon::ConnectClient(string host, int port) sockaddr_in sa = CreateAddrInet(host, port); sockaddr* psa = (sockaddr*)&sa; + { + // Check if trying to connect to self. + sockaddr_any lsa; + int size = lsa.size(); + srt_getsockname(m_sock, &lsa, &size); + + if (lsa.hport() == port && IsTargetAddrSelf(&lsa, psa)) + { + Verb() << "ERROR: Trying to connect to SELF address " << SockaddrToString(psa) + << " with socket bound to " << SockaddrToString(&lsa); + UDT::ERRORINFO inval(MJ_SETUP, MN_INVAL, 0); + Error(inval, "srt_connect"); + } + } + Verb() << "Connecting to " << host << ":" << port << " ... " << VerbNoEOL; int stat = srt_connect(m_sock, psa, sizeof sa); if ( stat == SRT_ERROR ) From 00e10c2f2828e11d6b5a7af0d5f945d4cefff0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 17 May 2018 13:28:26 +0200 Subject: [PATCH 007/517] Fixed build break on Windows --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ebbb5b89..c7e0e20d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -644,7 +644,7 @@ macro(srt_make_application name) target_link_libraries(${name} ${srt_link_library}) if (MICROSOFT) - target_link_libraries(${target} PRIVATE Iphlpapi.lib) + target_link_libraries(${name} PRIVATE Iphlpapi.lib) endif() endmacro() From 1d5bd086bbd09f782130ada9f510950ad3db3836 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Thu, 17 May 2018 16:15:33 +0200 Subject: [PATCH 008/517] Fixed incorrect implementation on Windows. Reading all families --- CMakeLists.txt | 11 +++++++---- apps/apputil.cpp | 24 +++++++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7e0e20d9..fe4102dd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -644,7 +644,7 @@ macro(srt_make_application name) target_link_libraries(${name} ${srt_link_library}) if (MICROSOFT) - target_link_libraries(${name} PRIVATE Iphlpapi.lib) + target_link_libraries(${name} Iphlpapi) endif() endmacro() @@ -724,9 +724,12 @@ if ( ENABLE_CXX11 ) endmacro() srt_add_testprogram(utility-test) - srt_add_testprogram(uriparser-test) - target_compile_options(uriparser-test PRIVATE -DTEST) - target_compile_options(uriparser-test PRIVATE ${CFLAGS_CXX_STANDARD}) + if (NOT WIN32) + # Block this for Windows because this causes weird GPF and symlink problems. + srt_add_testprogram(uriparser-test) + target_compile_options(uriparser-test PRIVATE -DTEST) + target_compile_options(uriparser-test PRIVATE ${CFLAGS_CXX_STANDARD}) + endif() srt_add_testprogram(srt-test-live) srt_make_application(srt-test-live) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index b9823fee4..e96be70ea 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -9,9 +9,9 @@ */ #include -// For Options #include #include +#include #include "apputil.hpp" #include "netinet_any.h" @@ -197,20 +197,24 @@ static vector GetLocalInterfaces() { vector locals; #ifdef WIN32 - ULONG family = s->m_pSelfAddr->sa_family; ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; - ULONG outBufLen = 0; + ULONG outBufLen4 = 0, outBufLen6 = 0, outBufLen = 0; // This function doesn't allocate memory by itself, you have to do it // yourself, worst case when it's too small, the size will be corrected // and the function will do nothing. So, simply, call the function with // always too little 0 size and make it show the correct one. - GetAdaptersAddresses(family, flags, NULL, NULL, &outBufLen); + GetAdaptersAddresses(AF_INET, flags, NULL, NULL, &outBufLen4); + GetAdaptersAddresses(AF_INET, flags, NULL, NULL, &outBufLen6); // Ignore errors. Check errors on the real call. + // (Have doubts about this "max" here, as VC reports errors when + // using std::max, so it will likely resolve to a macro - hope this + // won't cause portability problems, this code is Windows only. + outBufLen = max(outBufLen4, outBufLen6); // Good, now we can allocate memory PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)::operator new(outBufLen); - ULONG st = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + ULONG st = GetAdaptersAddresses(AF_INET, flags, NULL, pAddresses, &outBufLen); if (st == ERROR_SUCCESS) { PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; @@ -220,6 +224,16 @@ static vector GetLocalInterfaces() pUnicast = pUnicast->Next; } } + st = GetAdaptersAddresses(AF_INET6, flags, NULL, pAddresses, &outBufLen); + if (st == ERROR_SUCCESS) + { + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress; + while (pUnicast) + { + locals.push_back(pUnicast->Address.lpSockaddr); + pUnicast = pUnicast->Next; + } + } ::operator delete(pAddresses); From 6356b7660adbf98624fcc2966125d1ddcd3f6519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 9 Aug 2019 08:52:20 +0200 Subject: [PATCH 009/517] Fixed after merge --- apps/apputil.cpp | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 52b2dc40a..0e4c9941b 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -121,7 +121,8 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector { using namespace std; - string current_key = ""; + string current_key; + string extra_arg; size_t vals = 0; OptionScheme::Args type = OptionScheme::ARG_VAR; // This is for no-option-yet or consumed map> params; @@ -133,14 +134,8 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector // cout << "*D ARG: '" << a << "'\n"; if (moreoptions && a[0] == '-') { - string key(a + 1); // omit '-' - size_t pos = key.find_first_of(":"); - if (pos == string::npos) - pos = key.find(' '); - string value = pos == string::npos ? "" : key.substr(pos + 1); - key = key.substr(0, pos); - - current_key = key; + size_t seppos; // (see goto, it would jump over initialization) + current_key = a+1; if ( current_key == "-" ) { // The -- argument terminates the options. @@ -150,28 +145,51 @@ options_t ProcessOptions(char* const* argv, int argc, std::vector moreoptions = false; goto EndOfArgs; } + + // Maintain the backward compatibility with argument specified after : + // or with one string separated by space inside. + seppos = current_key.find(':'); + if (seppos == string::npos) + seppos = current_key.find(' '); + if (seppos != string::npos) + { + // Old option specification. + extra_arg = current_key.substr(seppos + 1); + current_key = current_key.substr(0, 0 + seppos); + } + params[current_key].clear(); vals = 0; + if (extra_arg != "") + { + params[current_key].push_back(extra_arg); + ++vals; + extra_arg.clear(); + } + // Find the key in the scheme. If not found, treat it as ARG_NONE. for (auto s: scheme) { if (s.names.count(current_key)) { // cout << "*D found '" << current_key << "' in scheme type=" << int(s.type) << endl; - if (s.type == OptionScheme::ARG_NONE ) + if (s.type == OptionScheme::ARG_NONE) { // Anyway, consider it already processed. break; } - else if (s.type == OptionScheme::ARG_ONE) + type = s.type; + + if ( vals == 1 && type == OptionScheme::ARG_ONE ) { - if (!value.empty()) - params[current_key].push_back(value); + // Argument for one-arg option already consumed, + // so set to free args. + goto EndOfArgs; } - type = s.type; goto Found; } + } // Not found: set ARG_NONE. // cout << "*D KEY '" << current_key << "' assumed type NONE\n"; From 1c1b4f278cde08cecb1b24fd5e90b68a1faea6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 9 Aug 2019 09:41:09 +0200 Subject: [PATCH 010/517] Fixed external inet_pton for mingw --- apps/apputil.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0e4c9941b..085dd0a33 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -37,8 +37,10 @@ using namespace std; // See: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742214(v=vs.85).aspx // http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html -#ifdef __MINGW32__ -static int inet_pton(int af, const char * src, void * dst) +#if defined(__MINGW32__) && !defined(InetPton) +namespace // Prevent conflict in case when still defined +{ +int inet_pton(int af, const char * src, void * dst) { struct sockaddr_storage ss; int ssSize = sizeof(ss); @@ -76,6 +78,7 @@ static int inet_pton(int af, const char * src, void * dst) return 0; } +} #endif // __MINGW__ sockaddr_in CreateAddrInet(const string& name, unsigned short port) From 46208500d68b057e906cd9bc1236cd7a3179ac05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 14 Aug 2019 12:34:57 +0200 Subject: [PATCH 011/517] Moved Iphlpapi lib to test programs only --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 571962746..56c60b9c5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -763,10 +763,6 @@ macro(srt_make_application name) target_link_libraries(${name} PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) endif() - if (MICROSOFT AND ENABLE_CONSELF_CHECK_WIN32) - target_link_libraries(${name} Iphlpapi) - add_definitions(-DSRT_ENABLE_CONSELF_CHECK_WIN32) - endif() if (srt_libspec_static AND CMAKE_DL_LIBS) target_link_libraries(${name} ${CMAKE_DL_LIBS}) endif() @@ -834,6 +830,10 @@ if (ENABLE_APPS) # list of source files in its own Manifest file. MafReadDir(testing ${name}.maf SOURCES SOURCES_app) srt_add_program(${name} ${SOURCES_app}) + if (MICROSOFT AND ENABLE_CONSELF_CHECK_WIN32) + target_link_libraries(${name} Iphlpapi) + add_definitions(-DSRT_ENABLE_CONSELF_CHECK_WIN32) + endif() endmacro() srt_add_testprogram(utility-test) From 99399ffadd1e7e597a928074c957cfe76bce3b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 14 Aug 2019 13:22:26 +0200 Subject: [PATCH 012/517] Some fixes for Windows --- CMakeLists.txt | 9 +++++---- apps/apputil.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c60b9c5..0aa707441 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ endif() option(CYGWIN_USE_POSIX "Should the POSIX API be used for cygwin. Ignored if the system isn't cygwin." OFF) option(ENABLE_CXX11 "Should the c++11 parts (srt-live-transmit) be enabled" ON) option(ENABLE_APPS "Should the Support Applications be Built?" ON) +option(ENABLE_TESTING "Should the Developer Test Applications be Built?" OFF) option(ENABLE_PROFILE "Should instrument the code for profiling. Ignored for non-GNU compiler." $ENV{HAI_BUILD_PROFILE}) option(ENABLE_LOGGING "Should logging be enabled" ON) option(ENABLE_HEAVY_LOGGING "Should heavy debug logging be enabled" ${ENABLE_HEAVY_LOGGING_DEFAULT}) @@ -732,6 +733,10 @@ macro(srt_add_program name) target_include_directories(${name} PRIVATE apps) target_include_directories(${name} PRIVATE common) install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if (MICROSOFT AND ENABLE_CONSELF_CHECK_WIN32) + target_link_libraries(${name} Iphlpapi) + add_definitions(-DSRT_ENABLE_CONSELF_CHECK_WIN32) + endif() endmacro() macro(srt_make_application name) @@ -830,10 +835,6 @@ if (ENABLE_APPS) # list of source files in its own Manifest file. MafReadDir(testing ${name}.maf SOURCES SOURCES_app) srt_add_program(${name} ${SOURCES_app}) - if (MICROSOFT AND ENABLE_CONSELF_CHECK_WIN32) - target_link_libraries(${name} Iphlpapi) - add_definitions(-DSRT_ENABLE_CONSELF_CHECK_WIN32) - endif() endmacro() srt_add_testprogram(utility-test) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index e4536320d..51c5f9cfb 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -240,7 +240,7 @@ static vector GetLocalInterfaces() { vector locals; #ifdef _WIN32 - ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; + ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_ALL_INTERFACES; ULONG outBufLen4 = 0, outBufLen6 = 0, outBufLen = 0; // This function doesn't allocate memory by itself, you have to do it From bc3e2ed879405b7da1c4a54da2941e0932ff65ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 8 Nov 2019 12:25:22 +0100 Subject: [PATCH 013/517] Removed empty line to avoid unnecessary changes --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c53739cc0..d3d687010 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -844,7 +844,6 @@ macro(srt_make_application name) if (USE_GNUSTL) target_link_libraries(${name} PRIVATE ${GNUSTL_LIBRARIES} ${GNUSTL_LDFLAGS}) endif() - if (srt_libspec_static AND CMAKE_DL_LIBS) target_link_libraries(${name} ${CMAKE_DL_LIBS}) endif() From 5e41749b9475a29378e8ddafc0f9b551f1b50d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 1 Apr 2020 18:54:45 +0200 Subject: [PATCH 014/517] Updated balancing documentation --- docs/API-functions.md | 29 ++++++++---- docs/bonding-intro.md | 8 ++++ docs/socket-groups.md | 104 +++++++++++++++++++++++++++--------------- 3 files changed, 96 insertions(+), 45 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index 4d4737da9..c89eca631 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -634,7 +634,7 @@ typedef struct SRT_SocketGroupData_ int result; struct sockaddr_storage srcaddr; struct sockaddr_storage peeraddr; // Don't want to expose sockaddr_any to public API - int priority; + int weight; } SRT_SOCKGROUPDATA; ``` @@ -645,14 +645,25 @@ where: * `result`: result of the operation (if this operation recently updated this structure) * `srcaddr`: address to which `id` should be bound * `peeraddr`: address to which `id` should be connected -* `priority`: priority for backup group - -The priority is set to 0 by default by `srt_prepare_endpoint()` - you can set -it to a different value afterwards. The default 0 value is the highest priority -and greater values declare lower priorities. The priority for the backup -groups determines which link is activated first when the currently active link is -unstable, and which should keep transmitting when multiple active links are -currently stable. This is not used by any other group types. +* `weight`: weight value + +The weight is set to 0 by default by `srt_prepare_endpoint()` - you can set +it to a different value afterwards. The meaning of weight depends on the group +type: + +1. Backup groups: in this case it defines the link priority. The default 0 +value is the highest priority and greater values declare lower priorities. The +priority for the backup groups determines which link is activated first when +the currently active link is unstable, and which should keep transmitting when +multiple active links are currently stable. + +2. Balancing groups with "fixed" algorighm: in this case it defines the +desired link load share. You can think of it as a percentage of link load, +but indeed a load percentage is defined as this weight value divided by a sum +of all weight values from all member links. Note however that the sum is +calculated out of all links that have been successfully connected. The +default 0 is also a special value that defines an "equalized" load share +(its set to the arithmetic average of the weights from all links). Functions to be used on groups: diff --git a/docs/bonding-intro.md b/docs/bonding-intro.md index e6845002a..1088482e9 100644 --- a/docs/bonding-intro.md +++ b/docs/bonding-intro.md @@ -20,6 +20,14 @@ used. How the links are utilized within a group depends on the group type. The simplest type, broadcast, utilizes all links at once to send the same data. +There are two main features for which the groups exist: + +1. Redundancy: keep the signal constantly delivered even if one of the +links gets unexpectedly broken. + +2. Balancing: send a stream with a bitrate that would be too high for +a single link, but enough if every link gets only a share of load. + To learn more about socket groups and their abilities, please read the [detailed document](socket-groups.md). diff --git a/docs/socket-groups.md b/docs/socket-groups.md index aabaa4f5e..6df11b398 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -56,14 +56,19 @@ Every next link in this group gives then another 100% overhead. ## 2. Backup This solution is more complicated and more challenging for the settings, -and in contradiction to Broadcast group, it costs some penalties. - -In this group, only one link out of member links is used for transmission -in a normal situation. Other links may start being used when there's happening -an event of "disturbance" on a link, which makes it considered "unstable". This -term is introduced beside "broken" because SRT normally uses 5 seconds to be -sure that the link is broken, and this is way too much to be used as a latency -penalty, if you still want to have a relatively low latency. +and in contradiction to Broadcast group, it costs some penalties. It has +also advantages: the overhead for redundancy, which in case of broadcast +groups is 100% per every next link, in this case it is tried to be kept at +negligible minimum. + +In this group, in a normal situation, only one link out of member links is used +for transmission, while others are just kept alive (the keepalive message +is sent over them usually once per 1 second). Other links may start being used +when there's happening an event of "disturbance" on a link, which makes it +considered "unstable". This term is introduced beside "broken" because SRT +normally uses 5 seconds to be sure that the link is broken, and this is way too +much to be used as a latency penalty, if you still want to have a relatively +low latency. Because of that there's a configurable timeout (with `SRTO_GROUPSTABTIMEO` option), which is the maximum time distance between two consecutive responses @@ -90,14 +95,31 @@ sending for a short time. This state should last at most as long as it takes for SRT to determie the link broken - either by getting the link broken by itself, or by closing the link when it's remaining unstable too long time. -This mode allows also to set link priorities - the lower, the more preferred. -This priority decides mainly, which link is "best" and which is selected to -take over transmission over a broken link before others, as well as which -links should remain active should multiple links be stable at a time. -If you don't specify priorities, the second connected link need not -take over sending, although as this is resolved through sorting, then -whichever link out of those with the same priority would take over when -all links are stable is undefined. +The overhead here may then come from two sources: +- the keepalive control packets for inactive links (negligible) +- sending data over an unstable link for the resolution time + +This means that the overhead is usually varrying, depending on how often +it happens that a link becomes unstable. It is then very important that +you tweak the value of stability timeout properly and cautiosly. If this +time is too small, your link might be too often too eagerly qualified as +unstable and with every case of unstable link it costs you temporary +overhead. Note that a single activation of a backup link costs you even +more than 100% overhead for the short time (up to 5 seconds), as usually some +packets that have been already sent over this unstable link, but weren't +acknowledged yet, will be sent again over the newly activated link. As +this is usually tolerable minimum overhead if it was due to a broken link, +you may experience link switching much more often and uselessly, if your +stability timeout is too small. + +This mode allows also to set link priorities, through the `weight` +parameter - the lower, the more preferred. This priority decides mainly, which +link is "best" and which is selected to take over transmission over a broken +link before others, as well as which links should remain active should multiple +links be stable at a time. If you don't specify priorities, the second +connected link need not take over sending, although as this is resolved through +sorting, then whichever link out of those with the same priority would take +over when all links are stable is undefined. Note that this group has an advantage over Broadcast in that it allows you to implement link redundancy with a very little overhead, as it keeps the @@ -153,14 +175,39 @@ protection, the mechanism should quickly detect a link as broken so that packets lost on the broken link can be resent over the others, but no such mechanism has been provided for balancing group. +Please also keep in mind that there's a rule for all groups - one member +link established is enough for a group to be connection established. This +might be not always wanted in case of balancing groups, so your application +might need to do additional check and monitor how many links and which +ones are currently active. For example, if you need 3 links to balance +the load and having only 2 of them would be too little to withstand the +whole transmission, you should wait with connecting until all 3 links +are established. Important thing here is also that one of the links might +be at some moment not possible to be established, or one of the links +can get broken during transmission. + As there could be various ways as to how to implement balancing algorithm, there's a framework provided to implement various methods, and two algorithms are currently provided: -1. `plain` (default). This is a simple round-robin - next link selected -to send the next packet is the oldest used so far. - -2. `window`. This algorithm is performing cyclic measurement of the +1. `fixed`. In this algorithm you specify the share that is given to +particular link manually through the `weight` parameter. The weight +value in case of this algorithm in balancing groups defines how much of +a share is given to this link. Important here is that you can easily +think of the weight values as a percentage of load burden for particular +link - however in reality the share of the load is calculated as a +percentage that particular link's weight comprises among the sum of all +weight values. Additionally, a value of 0 is special and is translated +into a weight that would make it equal to an average of all weights. +Be careful here with the non-established and broken links. For example, +if you have 3 links with weight 10, 20 and 30, it results in a load +balance of 16.6%, 33.3% and 50% respectively. However if the second link +gets broken, there are then 2 links with 10 and 30, which results in +load balance of 25% and 75% respectively. Keep in mind that it's up +to the application to keep the minimum links allowed and break the +group link that is unable to withstand the load. + +2. `window` (default). This algorithm is performing cyclic measurement of the minimum flight window and this way determines the "cost of sending" of a packet over particular link. The link is then "paid" for sending a packet appropriate "price", which is collected in the link's "pocket". @@ -168,22 +215,7 @@ To send the next packet the link with lowest state of the "pocket" is selected. The "cost of sending" measurement is being repeated once per a time with a distance of 16 packets on each link. -There are possible also other methods and algorithms, like: - -a) Explicit share definition. You declare, how much bandwidth you expect -the links to withstand as a percentage of the signal's bitrate. This -shall not exceed 100%. This is merely like the above Window algorithm, -but the "cost of sending" is defined by this percentage. - -b) Bandwidth measurement. This relies on the fact that the current -sending on particular link should use only some percentage of its -overall possible bandwidth. This requires a reliable way of measuring -the bandwidth, which is currently not good enough yet. This needs to -use a similar method as in "window" algorithm, that is, start with -equal round-robin and then perform actively a measurement and update -the cost of sending by assigning so much of a share of the signal -bitrte as it is represented by the share of the link in the sum of -all maximum bandwidth values from every link. +There are possible also other methods and algorithms in the future. ## 4. Multicast (NOT IMPLEMENTED - a concept) From b068c86051fc28d8dbf2fae594a24db3b0600adb Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Apr 2020 09:27:36 +0200 Subject: [PATCH 015/517] Post-review fixes phase #1 --- docs/API-functions.md | 4 ++-- docs/socket-groups.md | 40 ++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index c89eca631..eb2b43c2d 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -657,13 +657,13 @@ priority for the backup groups determines which link is activated first when the currently active link is unstable, and which should keep transmitting when multiple active links are currently stable. -2. Balancing groups with "fixed" algorighm: in this case it defines the +2. Balancing groups with "fixed" algorithm: in this case it defines the desired link load share. You can think of it as a percentage of link load, but indeed a load percentage is defined as this weight value divided by a sum of all weight values from all member links. Note however that the sum is calculated out of all links that have been successfully connected. The default 0 is also a special value that defines an "equalized" load share -(its set to the arithmetic average of the weights from all links). +(it's set to the arithmetic average of the weights from all links). Functions to be used on groups: diff --git a/docs/socket-groups.md b/docs/socket-groups.md index 6df11b398..139c23887 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -25,7 +25,7 @@ path. 2. Dispatch groups. -This category contains currently only one Multicast type (**CONCEPT!**). +This category contains currently only one Multicast type (__CONCEPT!__) Multicast group has a behavior dependent on the connection side and it is predicted to be only used in case when the listener side is a stream sender @@ -55,20 +55,22 @@ Every next link in this group gives then another 100% overhead. ## 2. Backup -This solution is more complicated and more challenging for the settings, -and in contradiction to Broadcast group, it costs some penalties. It has -also advantages: the overhead for redundancy, which in case of broadcast -groups is 100% per every next link, in this case it is tried to be kept at -negligible minimum. - -In this group, in a normal situation, only one link out of member links is used -for transmission, while others are just kept alive (the keepalive message -is sent over them usually once per 1 second). Other links may start being used -when there's happening an event of "disturbance" on a link, which makes it -considered "unstable". This term is introduced beside "broken" because SRT -normally uses 5 seconds to be sure that the link is broken, and this is way too -much to be used as a latency penalty, if you still want to have a relatively -low latency. +The functioning of this group type is more complicated and more challenging +for the user as it comes to using proper settings. Unlike Broadcast group type, +there are some penalties, but there are also advantages. Whereas the overhead +for redundancy in the case of broadcast groups is 100% per every next redundant +link, this is usually kept at a negligible minimum for backup groups. + +Under normal circumstances, only one of the member links in this group is used +for transmission, while the others are just kept alive (a keepalive message is +sent over these links usually once per 1 second). Other links may start being +used when a "disturbance" event occurs on a link, whereupon the link is +considered to be "unstable". There is a distinction to be made here between +"unstable" and "broken". SRT normally waits 5 seconds to be sure that a link is +broken, which is too much of a penalty if you want to have a relatively low +latency. SRT can react more quickly to an "unstable" link condition, as this +reaction must happen much faster than the latency time elapses, otherwise the +latency could not be kept up to. Because of that there's a configurable timeout (with `SRTO_GROUPSTABTIMEO` option), which is the maximum time distance between two consecutive responses @@ -116,7 +118,7 @@ This mode allows also to set link priorities, through the `weight` parameter - the lower, the more preferred. This priority decides mainly, which link is "best" and which is selected to take over transmission over a broken link before others, as well as which links should remain active should multiple -links be stable at a time. If you don't specify priorities, the second +links be stable at a time. If you don't specify priorities, the second connected link need not take over sending, although as this is resolved through sorting, then whichever link out of those with the same priority would take over when all links are stable is undefined. @@ -181,10 +183,12 @@ might be not always wanted in case of balancing groups, so your application might need to do additional check and monitor how many links and which ones are currently active. For example, if you need 3 links to balance the load and having only 2 of them would be too little to withstand the -whole transmission, you should wait with connecting until all 3 links +whole transmission, you should wait with transmitting until all 3 links are established. Important thing here is also that one of the links might be at some moment not possible to be established, or one of the links -can get broken during transmission. +can get broken during transmission - you might want to close the +connection even if the group connection doesn't break by itself, but +you don't have enough bandwidth coverage from existing links. As there could be various ways as to how to implement balancing algorithm, there's a framework provided to implement various methods, From 5dd2377be7f3e90163053e09ca60aebd169444bc Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Apr 2020 16:10:09 +0200 Subject: [PATCH 016/517] Post-review fix 2 Co-Authored-By: stevomatthews --- docs/socket-groups.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/socket-groups.md b/docs/socket-groups.md index 139c23887..9bc35badb 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -94,8 +94,8 @@ and remains ready to take over if there is a necessity. b) Unstable links continue to be used no matter that it may mean parallel sending for a short time. This state should last at most as long as it takes -for SRT to determie the link broken - either by getting the link broken by -itself, or by closing the link when it's remaining unstable too long time. +for SRT to determine the link broken - either by breaking the link by +itself, or by closing the link when it has been unstable too long. The overhead here may then come from two sources: - the keepalive control packets for inactive links (negligible) @@ -760,4 +760,3 @@ The stability timeout can be configured through `groupstabtimeo` option. Note that with increased stability timeout, the necessary latency penalty grows as well. - From 13e73651fb854297df8e7661ef9e5d398909a3c0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 6 Apr 2020 16:18:14 +0200 Subject: [PATCH 017/517] Post-review fix contd Co-Authored-By: stevomatthews --- docs/socket-groups.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/socket-groups.md b/docs/socket-groups.md index 9bc35badb..7d06cf974 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -101,18 +101,16 @@ The overhead here may then come from two sources: - the keepalive control packets for inactive links (negligible) - sending data over an unstable link for the resolution time -This means that the overhead is usually varrying, depending on how often -it happens that a link becomes unstable. It is then very important that -you tweak the value of stability timeout properly and cautiosly. If this -time is too small, your link might be too often too eagerly qualified as -unstable and with every case of unstable link it costs you temporary -overhead. Note that a single activation of a backup link costs you even -more than 100% overhead for the short time (up to 5 seconds), as usually some -packets that have been already sent over this unstable link, but weren't -acknowledged yet, will be sent again over the newly activated link. As -this is usually tolerable minimum overhead if it was due to a broken link, -you may experience link switching much more often and uselessly, if your -stability timeout is too small. +This means that the overhead is usually varying, depending on how often +a link becomes unstable. It is then very important that you tweak the value +of the stability timeout properly and cautiously. If this value is too small, +your link might be too eagerly qualified as unstable, costing temporary +overhead. Note that a single activation of a backup link costs more than +100% overhead for up to 5 seconds, since any packets already sent over +this unstable link (but not acknowledged) will be sent again over the newly +activated link. This is usually a tolerable minimum overhead when due to a +broken link. But you may experience unnecessary link switching much more +often if your stability timeout is too small. This mode allows also to set link priorities, through the `weight` parameter - the lower, the more preferred. This priority decides mainly, which @@ -759,4 +757,3 @@ when this link is back online. The stability timeout can be configured through `groupstabtimeo` option. Note that with increased stability timeout, the necessary latency penalty grows as well. - From de789ed6c6f04c4368fcc076f8dcd11563fba44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Apr 2020 18:50:00 +0200 Subject: [PATCH 018/517] Redesigned state information --- docs/socket-groups.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/socket-groups.md b/docs/socket-groups.md index 7d06cf974..a398002d3 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -33,6 +33,32 @@ with possibly multiple callers being stream receivers. It utilizes the UDP multicast feature in order to send payloads, while the control communication is still sent over the unicast link. +From the application point of view it is important to remember several rules +concerning groups: + +1. A group is connected when at least one member link is connected. +2. Disconnected links are removed from the group and are not reconnected. +3. The application may try to connect a new link at any time. This can also +succeed or fail, and only a successfully connected link becomes a member of +the group. + +In other words, links in socket groups are never "defined" - they can only be +"established". When they get broken, they are simply removed from the group. +It's up to the application to re-establish them. + +The group members can be also in appropriate states. The freshly created member +that is in the process of connecting is in "pending" state. When the connection +succeeds, it's in "idle" state. Then, when it's used for transmission, it's in +"active" state. If an operation on the link fails at any stage, it is removed +from the group. + +In Broadcast and Balancing group types, the "idle" links are activated once +they are found ready for sending as well as they report readiness for reading - +"idle" is only a temporary state between being freshly connected and being used +for transmission. In Backup groups it is possible for a link to turn from +"active" back to "idle" state. + + Details for the group types: @@ -175,6 +201,20 @@ protection, the mechanism should quickly detect a link as broken so that packets lost on the broken link can be resent over the others, but no such mechanism has been provided for balancing group. +Please also keep in mind that the group is considered connected when +it contains at least one connected member link. This means also that the +group becomes ready for transmission after connecting the first link, +as well as it remains ready even if some member links get broken. + +If the application wants to make sure that a transmission is balanced between +links (where only together can they maintain the bandwidth capacity required +for a signal), it must make sure that all "required" links are established by +monitoring the group data. For example, if you need a minimum of 3 links to +balance the load, you should delay starting the transmission until all 3 links +are established (that is, all of them report "idle" state), and also stop it in +case when a broken link caused that the others do not cover the required +capacity. + Please also keep in mind that there's a rule for all groups - one member link established is enough for a group to be connection established. This might be not always wanted in case of balancing groups, so your application From 280cd07de80078f327bc4d8213b02789eb8e386e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 7 Apr 2020 17:54:12 +0200 Subject: [PATCH 019/517] One more change --- docs/socket-groups.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/socket-groups.md b/docs/socket-groups.md index a398002d3..2fa73626c 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -81,11 +81,13 @@ Every next link in this group gives then another 100% overhead. ## 2. Backup -The functioning of this group type is more complicated and more challenging -for the user as it comes to using proper settings. Unlike Broadcast group type, -there are some penalties, but there are also advantages. Whereas the overhead -for redundancy in the case of broadcast groups is 100% per every next redundant -link, this is usually kept at a negligible minimum for backup groups. +The configuration of Backup groups is somewhat more complicated than with the +other group types. In particular, it may be challenging to arrive at the +optimal settings for a given set of network conditions and desired latency. +Unlike Broadcast group type, there are some penalties, but there are also +advantages. Whereas the overhead for redundancy in the case of broadcast groups +is 100% per every next redundant link, this is usually kept at a negligible +minimum for backup groups. Under normal circumstances, only one of the member links in this group is used for transmission, while the others are just kept alive (a keepalive message is @@ -239,8 +241,8 @@ a share is given to this link. Important here is that you can easily think of the weight values as a percentage of load burden for particular link - however in reality the share of the load is calculated as a percentage that particular link's weight comprises among the sum of all -weight values. Additionally, a value of 0 is special and is translated -into a weight that would make it equal to an average of all weights. +weight values. Additionally, a value of 0 is special and it is translated +into the arithmetic average of all non-zero weighted links. Be careful here with the non-established and broken links. For example, if you have 3 links with weight 10, 20 and 30, it results in a load balance of 16.6%, 33.3% and 50% respectively. However if the second link @@ -257,7 +259,8 @@ To send the next packet the link with lowest state of the "pocket" is selected. The "cost of sending" measurement is being repeated once per a time with a distance of 16 packets on each link. -There are possible also other methods and algorithms in the future. +The framework makes it easier to add also other balancing algorithms +in the future. ## 4. Multicast (NOT IMPLEMENTED - a concept) From 3f4672424d4db89a89584c22037ff12dd906cff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 15 Apr 2020 18:06:44 +0200 Subject: [PATCH 020/517] Added lacking information about obtaining group data and SRT_MSGCTRL --- docs/API-functions.md | 24 ++++++++++++++++++++++++ docs/API.md | 16 +++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index d6c877dcf..066c4f64e 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -663,6 +663,11 @@ calculated out of all links that have been successfully connected. The default 0 is also a special value that defines an "equalized" load share (it's set to the arithmetic average of the weights from all links). +The `SRT_SOCKGROUPDATA` structure is used in multiple purposes: + +* Prepare data for connection +* Getting the current member status + Functions to be used on groups: ### srt_create_group @@ -909,6 +914,8 @@ typedef struct SRT_MsgCtrl_ uint64_t srctime; // source timestamp (usec), 0: use internal time int32_t pktseq; // sequence number of the first packet in received message (unused for sending) int32_t msgno; // message number (output value for both sending and receiving) + SRT_SOCKGROUPDATA* grpdata; // pointer to group data array + size_t grpdata_size; // size of the group data array } SRT_MSGCTRL; ``` @@ -954,6 +961,23 @@ UDP packet, only the sequence of the first one is reported. Note that in although it is required that this value remain monotonic in subsequent send calls. Normally message numbers start with 1 and increase with every message sent. +* `grpdata`: Array to be filled by the group data state after the operation. +Only existing elements will be filled and only if the current number of members +isn't greater than the size specified in `grpdata_size`. + +* `grpdata_size`: the size of the `grpdata` array should be passed here when +calling the function. After the call this value should contain the actual +number of members in the group and the filled items in `grpdata` array. + +For more information about `SRT_SOCKGROUPDATA` and obtaining the group +data, please refer to [srt_group_data](#srt_group_data). Note that the +group data filling by `srt_sendmsg2` and `srt_recvmsg2` calls differs in one +aspect to `srt_group_data`: member sockets that were found broken after the +operation will appear in the group data with `SRTS_BROKEN` state once after the +operation was done, although the sockets assigned to these members are already +closed and they are removed as members already. In case of `srt_group_data` +they will not appear at all. + **Helpers for `SRT_MSGCTRL`:** ``` diff --git a/docs/API.md b/docs/API.md index 78a4bcd6e..4fc47830f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -190,7 +190,7 @@ object specifying extra data for the operation. Functions with the `msg2` suffix use the `SRT_MSGCTRL` object, and have the following interpretation (except `flags` and `boundary` that are reserved for -future use and should be 0): +future use): * `srt_sendmsg2`: * msgttl: [IN] maximum time (in ms) to wait for successful delivery (-1: indefinitely) @@ -205,6 +205,20 @@ future use and should be 0): * pktseq: [OUT] packet sequence number (first packet from the message, if it spans multiple UDP packets) * msgno: [OUT] message number assigned to the currently received message +* both above [IN] + * grpdata_size: size of the passed grpdata array + +* both above [OUT] + * grpdata: pointer to the array of group data to be filled by the call + * grpdata_size: actual size of the filled array + +**IMPORTANT**: no matter that fields are particularly marked as `[OUT]` (or +unused), values specified there could be used for input under certain +circumstances. Especially in `srt_sendmsg2` you should not reuse existing +objects of `SRT_MSGCTRL` type, but create always new ones and initialize them +with default `srt_msgctl_default` (or overwrite them first with and set the +desired values anew). + Please note that the `msgttl` and `inorder` arguments and fields in `SRT_MSGCTRL` are meaningful only when you use the message API in file mode (this will be explained later). In live mode, which is the SRT default, packets From f24b977cd38808de7758fda132046763a5d4a366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 20 Apr 2020 09:59:55 +0200 Subject: [PATCH 021/517] More fixes --- docs/socket-groups.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/socket-groups.md b/docs/socket-groups.md index 2fa73626c..b81c99ffb 100644 --- a/docs/socket-groups.md +++ b/docs/socket-groups.md @@ -230,9 +230,10 @@ can get broken during transmission - you might want to close the connection even if the group connection doesn't break by itself, but you don't have enough bandwidth coverage from existing links. -As there could be various ways as to how to implement balancing -algorithm, there's a framework provided to implement various methods, -and two algorithms are currently provided: +As there could be more than one way to implement a balancing +algorithm, there is a framework for to implementing various methods, +so that new algorithms are easier to provide in future. Currently +there are two algorithms provided: 1. `fixed`. In this algorithm you specify the share that is given to particular link manually through the `weight` parameter. The weight @@ -242,7 +243,8 @@ think of the weight values as a percentage of load burden for particular link - however in reality the share of the load is calculated as a percentage that particular link's weight comprises among the sum of all weight values. Additionally, a value of 0 is special and it is translated -into the arithmetic average of all non-zero weighted links. +into the arithmetic average of all non-zero weighted links, and if all +links have weight 0, all links have equal share. Be careful here with the non-established and broken links. For example, if you have 3 links with weight 10, 20 and 30, it results in a load balance of 16.6%, 33.3% and 50% respectively. However if the second link @@ -251,16 +253,12 @@ load balance of 25% and 75% respectively. Keep in mind that it's up to the application to keep the minimum links allowed and break the group link that is unable to withstand the load. -2. `window` (default). This algorithm is performing cyclic measurement of the +2. `window` (default). This algorithm performs cyclic measurement of the minimum flight window and this way determines the "cost of sending" -of a packet over particular link. The link is then "paid" for sending -a packet appropriate "price", which is collected in the link's "pocket". -To send the next packet the link with lowest state of the "pocket" is -selected. The "cost of sending" measurement is being repeated once per -a time with a distance of 16 packets on each link. - -The framework makes it easier to add also other balancing algorithms -in the future. +of a packet over a particular link. This evaluated cost is then added +to the current burden state of the link, and then the link with lowest burden +is selected to send the next packet. The "cost of sending" measurement is being +repeated once per a time at an interval of 16 packets on each link. ## 4. Multicast (NOT IMPLEMENTED - a concept) @@ -271,7 +269,7 @@ receiving a data stream sent from a stream server by multiple receivers. Multicast sending is using the feature of UDP multicast, however the connection concept is still in force. The concept of multicast groups -is predicted to facilitate the multicast abilities provided by the router +is intended to facilitate the multicast abilities provided by the router in the LAN, while still maintain the advantages of SRT. When you look at the difference that UDP multicast provides you towards From a81a5aa595426fad8005c2852b36577347e09a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 20 Apr 2020 10:07:28 +0200 Subject: [PATCH 022/517] Some more fixes --- docs/API-functions.md | 5 +++-- docs/API.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/API-functions.md b/docs/API-functions.md index 066c4f64e..39a851cd4 100644 --- a/docs/API-functions.md +++ b/docs/API-functions.md @@ -966,8 +966,9 @@ Only existing elements will be filled and only if the current number of members isn't greater than the size specified in `grpdata_size`. * `grpdata_size`: the size of the `grpdata` array should be passed here when -calling the function. After the call this value should contain the actual -number of members in the group and the filled items in `grpdata` array. +calling the function. After the call this field will contain the current +number of members in the group and the filled items in `grpdata` array, +regardless whether the array had enough size to fill all members or not. For more information about `SRT_SOCKGROUPDATA` and obtaining the group data, please refer to [srt_group_data](#srt_group_data). Note that the diff --git a/docs/API.md b/docs/API.md index 4fc47830f..d1e5ecc09 100644 --- a/docs/API.md +++ b/docs/API.md @@ -216,7 +216,7 @@ future use): unused), values specified there could be used for input under certain circumstances. Especially in `srt_sendmsg2` you should not reuse existing objects of `SRT_MSGCTRL` type, but create always new ones and initialize them -with default `srt_msgctl_default` (or overwrite them first with and set the +with default `srt_msgctl_default` (or overwrite them first with it and set the desired values anew). Please note that the `msgttl` and `inorder` arguments and fields in From 20043be2f712564130ae24135d8cf659c5c881e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Nov 2020 14:07:06 +0100 Subject: [PATCH 023/517] [core] Fixed a bug that responded a repeated conclusion HS with rejection --- srtcore/core.cpp | 274 +++++++++++++++++++++++++++++------------------ srtcore/core.h | 1 + 2 files changed, 170 insertions(+), 105 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 098198f46..e37cd1e77 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1587,7 +1587,7 @@ void CUDT::setListenState() m_bListening = true; } -size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int hs_version) +size_t CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgtype, int hs_version) { if (srtlen < SRT_HS_E_SIZE) { @@ -1598,24 +1598,24 @@ size_t CUDT::fillSrtHandshake(uint32_t *srtdata, size_t srtlen, int msgtype, int srtlen = SRT_HS_E_SIZE; // We use only that much space. - memset((srtdata), 0, sizeof(uint32_t) * srtlen); + memset((aw_srtdata), 0, sizeof(uint32_t) * srtlen); /* Current version (1.x.x) SRT handshake */ - srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ - srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); + aw_srtdata[SRT_HS_VERSION] = m_lSrtVersion; /* Required version */ + aw_srtdata[SRT_HS_FLAGS] |= SrtVersionCapabilities(); switch (msgtype) { case SRT_CMD_HSREQ: - return fillSrtHandshake_HSREQ(srtdata, srtlen, hs_version); + return fillSrtHandshake_HSREQ((aw_srtdata), srtlen, hs_version); case SRT_CMD_HSRSP: - return fillSrtHandshake_HSRSP(srtdata, srtlen, hs_version); + return fillSrtHandshake_HSRSP((aw_srtdata), srtlen, hs_version); default: LOGC(cnlog.Fatal, log << "IPE: fillSrtHandshake/sendSrtMsg called with value " << msgtype); return 0; } } -size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // INITIATOR sends HSREQ. @@ -1635,50 +1635,50 @@ size_t CUDT::fillSrtHandshake_HSREQ(uint32_t *srtdata, size_t /* srtlen - unused * Sent data is real-time, use Time-based Packet Delivery, * set option bit and configured delay */ - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; if (hs_version < CUDT::HS_VERSION_SRT1) { // HSv4 - this uses only one value. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iPeerTsbPdDelay_ms); } else { // HSv5 - this will be understood only since this version when this exists. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); // And in the reverse direction. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; - srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); // This wasn't there for HSv4, this setting is only for the receiver. // HSv5 is bidirectional, so every party is a receiver. if (m_bTLPktDrop) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; } } if (m_bRcvNakReport) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; // I support SRT_OPT_REXMITFLG. Do you? - srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; // Declare the API used. The flag is set for "stream" API because // the older versions will never set this flag, but all old SRT versions use message API. if (!m_bMessageAPI) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_STREAM; HLOGC(cnlog.Debug, - log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) - << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" - << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + log << "HSREQ/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); return 3; } -size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused */, int hs_version) +size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *aw_srtdata, size_t /* srtlen - unused */, int hs_version) { // Setting m_tsRcvPeerStartTime is done in processSrtMsg_HSREQ(), so // this condition will be skipped only if this function is called without @@ -1697,18 +1697,18 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused * We got and transposed peer start time (HandShake request timestamp), * we can support Timestamp-based Packet Delivery */ - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDRCV; if (hs_version < HS_VERSION_SRT1) { // HSv4 - this uses only one value - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_LEG::wrap(m_iTsbPdDelay_ms); } else { // HSv5 - this puts "agent's" latency into RCV field and "peer's" - // into SND field. - srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); + aw_srtdata[SRT_HS_LATENCY] = SRT_HS_LATENCY_RCV::wrap(m_iTsbPdDelay_ms); } } else @@ -1722,8 +1722,8 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused { // HSv5 is bidirectional - so send the TSBPDSND flag, and place also the // peer's latency into SND field. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; - srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TSBPDSND; + aw_srtdata[SRT_HS_LATENCY] |= SRT_HS_LATENCY_SND::wrap(m_iPeerTsbPdDelay_ms); HLOGC(cnlog.Debug, log << "HSRSP/snd: HSv5 peer uses TSBPD, responding TSBPDSND latency=" << m_iPeerTsbPdDelay_ms); @@ -1736,14 +1736,14 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused } if (m_bTLPktDrop) - srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_TLPKTDROP; if (m_bRcvNakReport) { // HSv5: Note that this setting is independent on the value of // m_bPeerNakReport, which represent this setting in the peer. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_NAKREPORT; /* * NAK Report is so efficient at controlling bandwidth that sender TLPktDrop * is not needed. SRT 1.0.5 to 1.0.7 sender TLPktDrop combined with SRT 1.0 @@ -1753,7 +1753,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused * from enabling Too-Late Packet Drop. */ if (m_lPeerSrtVersion <= SrtVersion(1, 0, 7)) - srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; + aw_srtdata[SRT_HS_FLAGS] &= ~SRT_OPT_TLPKTDROP; } if (m_lSrtVersion >= SrtVersion(1, 2, 0)) @@ -1767,7 +1767,7 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused else { // Request that the rexmit bit be used as a part of msgno. - srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; + aw_srtdata[SRT_HS_FLAGS] |= SRT_OPT_REXMITFLG; HLOGF(cnlog.Debug, "HSRSP/snd: AGENT UNDERSTANDS REXMIT flag and PEER reported that it does, too."); } } @@ -1779,9 +1779,9 @@ size_t CUDT::fillSrtHandshake_HSRSP(uint32_t *srtdata, size_t /* srtlen - unused } HLOGC(cnlog.Debug, - log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(srtdata[SRT_HS_LATENCY]) - << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY]) << "] FLAGS[" - << SrtFlagString(srtdata[SRT_HS_FLAGS]) << "]"); + log << "HSRSP/snd: LATENCY[SND:" << SRT_HS_LATENCY_SND::unwrap(aw_srtdata[SRT_HS_LATENCY]) + << " RCV:" << SRT_HS_LATENCY_RCV::unwrap(aw_srtdata[SRT_HS_LATENCY]) << "] FLAGS[" + << SrtFlagString(aw_srtdata[SRT_HS_FLAGS]) << "]"); return 3; } @@ -2207,7 +2207,7 @@ bool CUDT::createSrtHandshake( // ra_size after that // NOTE: so far, ra_size is m_iMaxSRTPayloadSize expressed in number of elements. // WILL BE CHANGED HERE. - ra_size = fillSrtHandshake(p + offset, total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); + ra_size = fillSrtHandshake((p + offset), total_ra_size - offset, srths_cmd, HS_VERSION_SRT1); *pcmdspec = HS_CMDSPEC_CMD::wrap(srths_cmd) | HS_CMDSPEC_SIZE::wrap(ra_size); HLOGC(cnlog.Debug, @@ -4319,6 +4319,85 @@ void CUDT::cookieContest() m_SrtHsSide = HSD_DRAW; } +// This function should complete the data for KMX needed for an out-of-band +// handshake response. Possibilities are: +// - There's no KMX (including first responder's handshake in rendezvous). This writes 0 to w_kmdatasize. +// - The encryption status is failure. Respond with fail code and w_kmdatasize = 1. +// - The last KMX was successful. Respond with the original kmdata and their size in w_kmdatasize. +EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) +{ + // If the last CONCLUSION message didn't contain the KMX extension, there's + // no key recorded yet, so it can't be extracted. Mark this w_kmdatasize empty though. + int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); + if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ)) + { + // This is a periodic handshake update, so you need to extract the KM data from the + // first message, provided that it is there. + size_t msgsize = m_pCryptoControl->getKmMsg_size(0); + if (msgsize == 0) + { + switch (m_pCryptoControl->m_RcvKmState) + { + // If the KMX process ended up with a failure, the KMX is not recorded. + // In this case as the KMRSP answer the "failure status" should be crafted. + case SRT_KM_S_NOSECRET: + case SRT_KM_S_BADSECRET: + { + HLOGC(cnlog.Debug, + log << "processRendezvous: No KMX recorded, status = " + << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it."); + + // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, + // that is, copy the NOSECRET code into KMX message. + memcpy((aw_kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); + w_kmdatasize = 1; + } + break; + + default: + // Remaining values: + // UNSECURED: should not fall here at alll + // SECURING: should not happen in HSv5 + // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) + { + m_RejectReason = SRT_REJ_IPE; + // Remaining situations: + // - password only on this site: shouldn't be considered to be sent to a no-password site + LOGC(cnlog.Error, + log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" + << KmStateStr(m_pCryptoControl->m_RcvKmState) + << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); + return CONN_REJECT; + } + break; + } + } + else + { + w_kmdatasize = msgsize / 4; + if (msgsize > w_kmdatasize * 4) + { + // Sanity check + LOGC(cnlog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); + memset((aw_kmdata + (w_kmdatasize * 4)), 0, msgsize - (w_kmdatasize * 4)); + ++w_kmdatasize; + } + + HLOGC(cnlog.Debug, + log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + << w_kmdatasize); + memcpy((aw_kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); + } + } + else + { + HLOGC(cnlog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); + w_kmdatasize = 0; + } + + return CONN_ACCEPT; +} + EConnectStatus CUDT::processRendezvous( const CPacket& response, const sockaddr_any& serv_addr, bool synchro, EReadStatus rst, CPacket& w_reqpkt) @@ -4415,73 +4494,11 @@ EConnectStatus CUDT::processRendezvous( } else { - // If the last CONCLUSION message didn't contain the KMX extension, there's - // no key recorded yet, so it can't be extracted. Mark this kmdatasize empty though. - int hs_flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_ConnRes.m_iType); - if (IsSet(hs_flags, CHandShake::HS_EXT_KMREQ)) - { - // This is a periodic handshake update, so you need to extract the KM data from the - // first message, provided that it is there. - size_t msgsize = m_pCryptoControl->getKmMsg_size(0); - if (msgsize == 0) - { - switch (m_pCryptoControl->m_RcvKmState) - { - // If the KMX process ended up with a failure, the KMX is not recorded. - // In this case as the KMRSP answer the "failure status" should be crafted. - case SRT_KM_S_NOSECRET: - case SRT_KM_S_BADSECRET: - { - HLOGC(cnlog.Debug, - log << "processRendezvous: No KMX recorded, status = NOSECRET. Respond with NOSECRET."); - - // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, - // that is, copy the NOSECRET code into KMX message. - memcpy((kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); - kmdatasize = 1; - } - break; - - default: - // Remaining values: - // UNSECURED: should not fall here at alll - // SECURING: should not happen in HSv5 - // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) - { - m_RejectReason = SRT_REJ_IPE; - // Remaining situations: - // - password only on this site: shouldn't be considered to be sent to a no-password site - LOGC(cnlog.Error, - log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" - << KmStateStr(m_pCryptoControl->m_RcvKmState) - << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); - return CONN_REJECT; - } - break; - } - } - else - { - kmdatasize = msgsize / 4; - if (msgsize > kmdatasize * 4) - { - // Sanity check - LOGC(cnlog.Error, log << "IPE: KMX data not aligned to 4 bytes! size=" << msgsize); - memset((kmdata + (kmdatasize * 4)), 0, msgsize - (kmdatasize * 4)); - ++kmdatasize; - } - - HLOGC(cnlog.Debug, - log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" - << kmdatasize); - memcpy((kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); - } - } - else - { - HLOGC(cnlog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); - kmdatasize = 0; - } + // This is a repeated handshake, so you can't use the incoming data to + // prepare data for createSrtHandshake. They have to be extracted from inside. + EConnectStatus conn = craftKmResponse((kmdata), (kmdatasize)); + if (conn != CONN_ACCEPT) + return conn; } // No matter the value of needs_extension, the extension is always needed @@ -8538,7 +8555,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) CHandShake req; req.load_from(ctrlpkt.m_pcData, ctrlpkt.getLength()); - HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); + HLOGC(inlog.Debug, log << CONID() << "processCtrl: got HS: " << req.show()); if ((req.m_iReqType > URQ_INDUCTION_TYPES) // acually it catches URQ_INDUCTION and URQ_ERROR_* symbols...??? || (m_bRendezvous && (req.m_iReqType != URQ_AGREEMENT))) // rnd sends AGREEMENT in rsp to CONCLUSION @@ -8573,7 +8590,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "processCtrl/HS: got HS reqtype=" << RequestTypeStr(req.m_iReqType) << " WITH SRT ext"); - have_hsreq = interpretSrtHandshake(req, ctrlpkt, kmdata, &kmdatasize); + have_hsreq = interpretSrtHandshake(req, ctrlpkt, (kmdata), (&kmdatasize)); if (!have_hsreq) { initdata.m_iVersion = 0; @@ -8611,6 +8628,7 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) else { initdata.m_iVersion = HS_VERSION_UDT4; + kmdatasize = 0; // HSv4 doesn't add any extensions, no KMX } initdata.m_extension = have_hsreq; @@ -8619,7 +8637,6 @@ void CUDT::processCtrl(const CPacket &ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - // XXX here interpret SRT handshake extension CPacket response; response.setControl(UMSG_HANDSHAKE); response.allocate(m_iMaxSRTPayloadSize); @@ -10564,9 +10581,54 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // reused for the connection rejection response (see URQ_ERROR_REJECT set // as m_iReqType). + if (result == 0) + { + // This is an existing connection, so the handshake is only needed + // because of the rule that every handshake request must be covered + // by the handshake response. It wouldn't be good to call interpretSrtHandshake + // here because the data from the handshake have been already interpreted + // and recorded. We just need to craft a response. + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: sending REPEATED handshake response req=" + << RequestTypeStr(hs.m_iReqType)); + + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + EConnectStatus conn = CONN_ACCEPT; + + if (hs.m_iVersion >= HS_VERSION_SRT1) + { + conn = craftKmResponse((kmdata), (kmdatasize)); + } + else + { + kmdatasize = 0; + } + + if (conn != CONN_ACCEPT) + return conn; + + packet.setLength(m_iMaxSRTPayloadSize); + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, + kmdata, kmdatasize, + (packet), (m_ConnReq))) + { + HLOGC(cnlog.Debug, + log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); + result = -1; // enforce fallthrough for the below condition! + hs.m_iReqType = URQFailure(m_RejectReason == SRT_REJ_UNKNOWN ? SRT_REJ_IPE : m_RejectReason); + } + else + { + // Send the crafted handshake + HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); + m_pSndQueue->sendto(addr, packet); + } + } + // send back a response if connection failed or connection already existed - // new connection response should be sent in acceptAndRespond() - if (result != 1) + // (or the above procedure failed) + if (result == -1) { HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: sending ABNORMAL handshake info req=" @@ -10579,6 +10641,8 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); } + // new connection response should be sent in acceptAndRespond() + // turn the socket writable if this is the first time when this was found out. else { // a new connection has been created, enable epoll for write diff --git a/srtcore/core.h b/srtcore/core.h index 962462100..d33026b64 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -551,6 +551,7 @@ class CUDT void applyResponseSettings() ATR_NOEXCEPT; SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT; SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr); + SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize); void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield); From 71268420be6032eb2478b11a0a43338d5357c005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 24 Nov 2020 12:01:55 +0100 Subject: [PATCH 024/517] Minor comment update --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 761bfdc33..5a8c193b1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4420,11 +4420,11 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) memcpy((aw_kmdata), &m_pCryptoControl->m_RcvKmState, sizeof(int32_t)); w_kmdatasize = 1; } - break; + break; // Treat as ACCEPT in general; might change to REJECT on enforced-encryption default: // Remaining values: - // UNSECURED: should not fall here at alll + // UNSECURED: should not fall here at all // SECURING: should not happen in HSv5 // SECURED: should have received the recorded KMX correctly (getKmMsg_size(0) > 0) { From 6226f3556319fc05b18664cefda5fde2a8c73f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 24 Nov 2020 17:42:23 +0100 Subject: [PATCH 025/517] Fixed proper crafting of the repeated handshake response --- srtcore/api.cpp | 7 ++++++- srtcore/api.h | 2 +- srtcore/core.cpp | 31 ++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 2715ae7b6..66b09b2f9 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -490,9 +490,10 @@ SRTSOCKET CUDTUnited::newSocket(CUDTSocket** pps) } int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, - CHandShake& w_hs, int& w_error) + CHandShake& w_hs, int& w_error, CUDT*& w_acpu) { CUDTSocket* ns = NULL; + w_acpu = NULL; w_error = SRT_REJ_IPE; @@ -534,6 +535,10 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, w_hs.m_iReqType = URQ_CONCLUSION; w_hs.m_iID = ns->m_SocketID; + // Report the original UDT because it will be + // required to complete the HS data for conclusion response. + w_acpu = ns->m_pUDT; + return 0; //except for this situation a new connection should be started diff --git a/srtcore/api.h b/srtcore/api.h index bce15c9c1..1a4927c5c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -232,7 +232,7 @@ friend class CRendezvousQueue; /// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error. int newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, - CHandShake& w_hs, int& w_error); + CHandShake& w_hs, int& w_error, CUDT*& w_acpu); int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 5a8c193b1..99f6832d0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4412,7 +4412,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) case SRT_KM_S_BADSECRET: { HLOGC(cnlog.Debug, - log << "processRendezvous: No KMX recorded, status = " + log << "craftKmResponse: No KMX recorded, status = " << KmStateStr(m_pCryptoControl->m_RcvKmState) << ". Respond it."); // Just do the same thing as in CCryptoControl::processSrtMsg_KMREQ for that case, @@ -4432,7 +4432,7 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) // Remaining situations: // - password only on this site: shouldn't be considered to be sent to a no-password site LOGC(cnlog.Error, - log << "processRendezvous: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" + log << "craftKmResponse: IPE: PERIODIC HS: NO KMREQ RECORDED KMSTATE: RCV=" << KmStateStr(m_pCryptoControl->m_RcvKmState) << " SND=" << KmStateStr(m_pCryptoControl->m_SndKmState)); return CONN_REJECT; @@ -4452,14 +4452,14 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) } HLOGC(cnlog.Debug, - log << "processRendezvous: getting KM DATA from the fore-recorded KMX from KMREQ, size=" + log << "craftKmResponse: getting KM DATA from the fore-recorded KMX from KMREQ, size=" << w_kmdatasize); memcpy((aw_kmdata), m_pCryptoControl->getKmMsg_data(0), msgsize); } } else { - HLOGC(cnlog.Debug, log << "processRendezvous: no KMX flag - not extracting KM data for KMRSP"); + HLOGC(cnlog.Debug, log << "craftKmResponse: no KMX flag - not extracting KM data for KMRSP"); w_kmdatasize = 0; } @@ -10813,7 +10813,8 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) else { int error = SRT_REJ_UNKNOWN; - int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error)); + CUDT* acpu = NULL; + int result = s_UDTUnited.newConnection(m_SocketID, addr, packet, (hs), (error), (acpu)); // This is listener - m_RejectReason need not be set // because listener has no functionality of giving the app @@ -10853,7 +10854,17 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // reused for the connection rejection response (see URQ_ERROR_REJECT set // as m_iReqType). - if (result == 0) + // The 'acpu' should be set to a new socket, if found; + // this means simultaneously that result == 0, but it's safest to + // check this condition only. This means that 'newConnection' found + // that the connection attempt has already been accepted, just the + // caller side somehow didn't get the answer. The rule is that every + // connection request HS must be completed with a symmetric HS response, + // so craft one here. + + // Note that this function runs in the listener socket context, while 'acpu' + // is the CUDT entity for the accepted socket. + if (acpu) { // This is an existing connection, so the handshake is only needed // because of the rule that every handshake request must be covered @@ -10870,7 +10881,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iVersion >= HS_VERSION_SRT1) { - conn = craftKmResponse((kmdata), (kmdatasize)); + // Always attach extension. + hs.m_extension = true; + conn = acpu->craftKmResponse((kmdata), (kmdatasize)); } else { @@ -10881,9 +10894,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) return conn; packet.setLength(m_iMaxSRTPayloadSize); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, + if (!acpu->createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (packet), (m_ConnReq))) + (packet), (hs))) { HLOGC(cnlog.Debug, log << "processConnectRequest: rejecting due to problems in createSrtHandshake."); From f68578d901a47e04d79e76c25c0ff5b179ca705d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 27 Nov 2020 11:17:28 +0100 Subject: [PATCH 026/517] Completed doxygen changes --- srtcore/api.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 1a4927c5c..8ad6bb3fd 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -229,6 +229,8 @@ friend class CRendezvousQueue; /// @param [in] listen the listening UDT socket; /// @param [in] peer peer address. /// @param [in,out] hs handshake information from peer side (in), negotiated value (out); + /// @param [out] w_error error code when failed + /// @param [out] w_acpu entity of accepted socket, if connection already exists /// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error. int newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, From 8e8cdac4ac73d5f066405b052bcf6f56471e7160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Nov 2020 09:32:17 +0100 Subject: [PATCH 027/517] Prepared changes for dispatch-to-acceptor (no build) --- srtcore/core.cpp | 17 +++++++++++++---- srtcore/core.h | 2 ++ srtcore/queue.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/queue.h | 10 ++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 414b51765..ce0d75c51 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5997,6 +5997,14 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_pRNode->m_bOnList = true; m_pRcvQueue->setNewEntry(this); + if (!createSendHSResponse(kmdata, kmdatasize, peer, (w_hs))) + { + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } +} + +bool CUDT::createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& peer, CHandShake& w_hs) ATR_NOTHROW +{ // send the response to the peer, see listen() for more discussions about this // XXX Here create CONCLUSION RESPONSE with: // - just the UDT handshake, if HS_VERSION_UDT4, @@ -6010,11 +6018,11 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, - log << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); + log << "createSendHSResponse: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) { - LOGC(cnlog.Error, log << "acceptAndRespond: error creating handshake response"); - throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + LOGC(cnlog.Error, log << "createSendHSResponse: error creating handshake response"); + return false; } // Set target socket ID to the value from received handshake's source ID. @@ -6027,7 +6035,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, CHandShake debughs; debughs.load_from(response.m_pcData, response.getLength()); HLOGC(cnlog.Debug, - log << CONID() << "acceptAndRespond: sending HS from agent @" + log << CONID() << "createSendHSResponse: sending HS from agent @" << debughs.m_iID << " to peer @" << response.m_iID << "HS:" << debughs.show()); } @@ -6039,6 +6047,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // coming as connected, but continue repeated handshake until finally // received the listener's handshake. m_pSndQueue->sendto(peer, response); + return true; } // This function is required to be called when a caller receives an INDUCTION diff --git a/srtcore/core.h b/srtcore/core.h index cc4d8ccf2..911619146 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -365,6 +365,7 @@ class CUDT } SRTSOCKET socketID() const { return m_SocketID; } + SRTSOCKET peerID() const { return m_PeerID; } static CUDT* getUDTHandle(SRTSOCKET u); static std::vector existingSockets(); @@ -599,6 +600,7 @@ class CUDT /// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out). void acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& hs); + bool createSendHSResponse(uint32_t* kmdata, size_t kmdatasize, const sockaddr_any& peer, CHandShake& w_hs) ATR_NOTHROW; bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt); /// Close the opened UDT entity. diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 3f7f18e13..8d34cddb7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -773,16 +773,27 @@ CUDT *CHash::lookup(int32_t id) return NULL; } +CUDT* CHash::lookupPeer(int32_t peerid) +{ + // Decode back the socket ID if it has that peer + int32_t id = map_get(m_RevPeerMap, peerid, -1); + if (id == -1) + return NULL; // no such peer id + return lookup(id); +} + void CHash::insert(int32_t id, CUDT *u) { CBucket *b = m_pBucket[id % m_iHashSize]; CBucket *n = new CBucket; n->m_iID = id; + n->m_iPeerID = u->peerID(); n->m_pUDT = u; n->m_pNext = b; m_pBucket[id % m_iHashSize] = n; + m_RevPeerMap[u->peerID()] = id; } void CHash::remove(int32_t id) @@ -799,6 +810,7 @@ void CHash::remove(int32_t id) else p->m_pNext = b->m_pNext; + m_RevPeerMap.erase(b->m_iPeerID); delete b; return; @@ -1402,11 +1414,44 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } + else + { + if (worker_TryAcceptedSocket(unit, addr)) + return CONN_CONTINUE; + } // If there's no listener waiting for the packet, just store it into the queue. return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. } +bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) +{ + // We are working with a possibly HS packet... check that. + CPacket& pkt = unit->m_Packet; + + if (pkt.getLength() < CHandShake::m_iContentSize || !pkt.isControl(UMSG_HANDSHAKE)) + return false; + + CHandShake hs; + if (0 != hs.load_from(pkt.data(), pkt.size())) + return false; + + if (hs.m_iReqType != URQ_CONCLUSION) + return false; + + // Ok, at last we have a peer ID info + int32_t peerid = hs.m_iID; + + // Now search for a socket that has this peer ID + CUDT* u = m_pHash->lookupPeer(peerid); + if (!u) + return false; // no socket has that peer in this multiplexer + + // CRAFT KMX DATA FOR RESPONSE + + return u->createSendHSResponse(kmdata, kmdatasize, (hs)); +} + EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) { CUDT *u = m_pHash->lookup(id); diff --git a/srtcore/queue.h b/srtcore/queue.h index d14b8728a..d2dfb9cdf 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -292,6 +292,12 @@ class CHash CUDT* lookup(int32_t id); + /// Look for a UDT instance from the hash table by source ID + /// @param [in] peerid socket ID of the peer reported as source ID + /// @return Pointer to a UDT instance where m_PeerID == peerid, or NULL if not found + + CUDT* lookupPeer(int32_t peerid); + /// Insert an entry to the hash table. /// @param [in] id socket ID /// @param [in] u pointer to the UDT instance @@ -307,6 +313,7 @@ class CHash struct CBucket { int32_t m_iID; // Socket ID + int32_t m_iPeerID; // Peer ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -314,6 +321,8 @@ class CHash int m_iHashSize; // size of hash table + std::map m_RevPeerMap; + private: CHash(const CHash&); CHash& operator=(const CHash&); @@ -489,6 +498,7 @@ friend class CUDTUnited; EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); + bool worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr); private: CUnitQueue m_UnitQueue; // The received packet queue From 84e37b6877af5a1c3f50121b85aa222662a910e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Nov 2020 16:37:01 +0100 Subject: [PATCH 028/517] Added crafting KMX response to call handshake --- srtcore/queue.cpp | 7 +++++-- testing/testmedia.cpp | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8d34cddb7..487502b1e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1447,9 +1447,12 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (!u) return false; // no socket has that peer in this multiplexer - // CRAFT KMX DATA FOR RESPONSE + uint32_t kmdata[SRTDATA_MAXSIZE]; + size_t kmdatasize = SRTDATA_MAXSIZE; + if (u->craftKmResponse((kmdata), (kmdatasize)) == CONN_ACCEPT) + return false; - return u->createSendHSResponse(kmdata, kmdatasize, (hs)); + return u->createSendHSResponse(kmdata, kmdatasize, addr, (hs)); } EConnectStatus CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 4c5a92462..d9861a96c 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -545,6 +545,9 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + /// TESTING + //srt_close(m_bindsock); + #if ENABLE_EXPERIMENTAL_BONDING if (m_sock & SRTGROUP_MASK) { From 80a4cbf4e5fa8727cb9cfe70e35797b256315503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Dec 2020 11:03:46 +0100 Subject: [PATCH 029/517] Fixed HS ext for HSv5. Blocked testing stuff --- srtcore/core.cpp | 10 ++++++++++ srtcore/queue.cpp | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6fa9682f7..7de2f0ce7 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4992,6 +4992,16 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE if (m_ConnRes.m_iVersion < HS_VERSION_SRT1) m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly in SRT HS. + /// TESTING - unblock if necessary. + /// This part fictionally "loses" incoming conclusion HS 5 times. + /* + static int fail_count = 5; + if (--fail_count) + { + LOGC(cnlog.Note, log << "postConnect: FAKE LOSS HS conclusion message"); + return CONN_CONTINUE; + } // */ + // This procedure isn't being executed in rendezvous because // in rendezvous it's completed before calling this function. if (!rendezvous) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 4064b06a0..77e1ddaa7 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1448,7 +1448,14 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc else { if (worker_TryAcceptedSocket(unit, addr)) + { + HLOGC(cnlog.Debug, log << "connection request to existing peer succeeded"); return CONN_CONTINUE; + } + else + { + HLOGC(cnlog.Debug, log << "connection request to an accepted socket failed. Will retry RDV or store"); + } } // If there's no listener waiting for the packet, just store it into the queue. @@ -1470,6 +1477,9 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (hs.m_iReqType != URQ_CONCLUSION) return false; + if (hs.m_iVersion >= CUDT::HS_VERSION_SRT1) + hs.m_extension = true; + // Ok, at last we have a peer ID info int32_t peerid = hs.m_iID; @@ -1478,10 +1488,16 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (!u) return false; // no socket has that peer in this multiplexer + HLOGC(cnlog.Debug, log << "FOUND accepted socket @" << u->m_SocketID << " that is a peer for -@" + << peerid << " - DISPATCHING to it to resend HS response"); + uint32_t kmdata[SRTDATA_MAXSIZE]; size_t kmdatasize = SRTDATA_MAXSIZE; - if (u->craftKmResponse((kmdata), (kmdatasize)) == CONN_ACCEPT) + if (u->craftKmResponse((kmdata), (kmdatasize)) != CONN_ACCEPT) + { + HLOGC(cnlog.Debug, log << "craftKmResponse: failed"); return false; + } return u->createSendHSResponse(kmdata, kmdatasize, addr, (hs)); } From 12b745556b733267c8bc487afe9ebd09acb2db78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 12:04:17 +0100 Subject: [PATCH 030/517] Fixed: rewritten negotiated values into the handshake and properly sent --- srtcore/core.cpp | 60 +++++++++++++++++++++++++----------------------- srtcore/core.h | 11 +++++++-- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 99f6832d0..68dc1119c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5874,6 +5874,25 @@ bool CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUD return true; } +void CUDT::rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs) +{ + // this is a reponse handshake + w_hs.m_iReqType = URQ_CONCLUSION; + w_hs.m_iMSS = m_iMSS; + w_hs.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; + w_hs.m_iID = m_SocketID; + + if (w_hs.m_iVersion > HS_VERSION_UDT4) + { + // The version is agreed; this code is executed only in case + // when AGENT is listener. In this case, conclusion response + // must always contain HSv5 handshake extensions. + w_hs.m_extension = true; + } + + CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); +} + void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs) { HLOGC(cnlog.Debug, log << "acceptAndRespond: setting up data according to handshake"); @@ -5883,45 +5902,28 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, m_tsRcvPeerStartTime = steady_clock::time_point(); // will be set correctly at SRT HS // Uses the smaller MSS between the peers - if (w_hs.m_iMSS > m_iMSS) - w_hs.m_iMSS = m_iMSS; - else - m_iMSS = w_hs.m_iMSS; + m_iMSS = std::min(m_iMSS, w_hs.m_iMSS); // exchange info for maximum flow window size - m_iFlowWindowSize = w_hs.m_iFlightFlagSize; - w_hs.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize; - + m_iFlowWindowSize = w_hs.m_iFlightFlagSize; m_iPeerISN = w_hs.m_iISN; - - setInitialRcvSeq(m_iPeerISN); - m_iRcvCurrPhySeqNo = w_hs.m_iISN - 1; + setInitialRcvSeq(m_iPeerISN); + m_iRcvCurrPhySeqNo = CSeqNo::decseq(w_hs.m_iISN); m_PeerID = w_hs.m_iID; - w_hs.m_iID = m_SocketID; // use peer's ISN and send it back for security check m_iISN = w_hs.m_iISN; - setInitialSndSeq(m_iISN); + setInitialSndSeq(m_iISN); m_SndLastAck2Time = steady_clock::now(); - // this is a reponse handshake - w_hs.m_iReqType = URQ_CONCLUSION; - - if (w_hs.m_iVersion > HS_VERSION_UDT4) - { - // The version is agreed; this code is executed only in case - // when AGENT is listener. In this case, conclusion response - // must always contain HSv5 handshake extensions. - w_hs.m_extension = true; - } - // get local IP address and send the peer its IP address (because UDP cannot get local IP address) memcpy((m_piSelfIP), w_hs.m_piPeerIP, sizeof m_piSelfIP); m_parent->m_SelfAddr = agent; CIPAddress::pton((m_parent->m_SelfAddr), m_piSelfIP, agent.family(), peer); - CIPAddress::ntop(peer, (w_hs.m_piPeerIP)); + + rewriteHandshakeData(peer, (w_hs)); int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE; m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -6034,9 +6036,6 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } - // Set target socket ID to the value from received handshake's source ID. - response.m_iID = m_PeerID; - #if ENABLE_HEAVY_LOGGING { // To make sure what REALLY is being sent, parse back the handshake @@ -6055,7 +6054,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - m_pSndQueue->sendto(peer, response); + addressAndSend((response)); } // This function is required to be called when a caller receives an INDUCTION @@ -10875,6 +10874,9 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) log << CONID() << "processConnectRequest: sending REPEATED handshake response req=" << RequestTypeStr(hs.m_iReqType)); + // Rewrite already updated previously data in acceptAndRespond + acpu->rewriteHandshakeData(acpu->m_PeerAddr, (hs)); + uint32_t kmdata[SRTDATA_MAXSIZE]; size_t kmdatasize = SRTDATA_MAXSIZE; EConnectStatus conn = CONN_ACCEPT; @@ -10907,7 +10909,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // Send the crafted handshake HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING (repeated) HS (a): " << hs.show()); - m_pSndQueue->sendto(addr, packet); + acpu->addressAndSend((packet)); } } diff --git a/srtcore/core.h b/srtcore/core.h index 005a32d08..0bb7d59d1 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -595,11 +595,18 @@ class CUDT void checkNeedDrop(bool& bCongestion); - /// Connect to a UDT entity listening at address "peer", which has sent "hs" request. + /// Connect to a UDT entity as per hs request. This will update + /// required data in the entity, then update them also in the hs structure, + /// and then send the response back to the caller. + /// @param agent [in] The address to which the UDT entity is bound. /// @param peer [in] The address of the listening UDT entity. + /// @param hspkt [in] The original packet that brought the handshake. /// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out). - void acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& hs); + + /// Write back to the hs structure the data after they have been + /// negotiated by acceptAndRespond. + void rewriteHandshakeData(const sockaddr_any& peer, CHandShake& w_hs); bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake& hs, const CPacket& hspkt); /// Close the opened UDT entity. From bb7b41c9923ab86cfdf8ba46f84eff2ca8693851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 14:17:46 +0100 Subject: [PATCH 031/517] Removed testing entry --- srtcore/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 53c2cbf24..dd5cb77a3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -6040,7 +6040,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer, // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - return; + //return; if (!createSendHSResponse(kmdata, kmdatasize, (w_hs))) { From abfbf441475a4fb6e0b0289b6f462573fa035861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Dec 2020 14:26:56 +0100 Subject: [PATCH 032/517] Fixed: do not try accepted socket if wrong address --- srtcore/queue.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index ef4b61d47..46715a539 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1449,7 +1449,7 @@ EConnectStatus CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, const soc { if (worker_TryAcceptedSocket(unit, addr)) { - HLOGC(cnlog.Debug, log << "connection request to existing peer succeeded"); + HLOGC(cnlog.Debug, log << "connection request to an accepted socket succeeded"); return CONN_CONTINUE; } else @@ -1505,7 +1505,8 @@ bool CRcvQueue::worker_TryAcceptedSocket(CUnit* unit, const sockaddr_any& addr) if (addr != u->m_PeerAddr) { HLOGC(cnlog.Debug, log << "worker_TryAcceptedSocket: accepted socket has a different address: " - << u->m_PeerAddr.str() << " than the incoming HS request: " << addr.str()); + << u->m_PeerAddr.str() << " than the incoming HS request: " << addr.str() << " - POSSIBLE ATTACK"); + return false; } return u->createSendHSResponse(kmdata, kmdatasize, (hs)); From 3f6e6acb11d9ace119cdbbd45e396390638c2a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 1 Jun 2021 17:32:37 +0200 Subject: [PATCH 033/517] [core] Fixed: closing socket should mark and signal so that srt_connect call can exit immediately --- srtcore/api.cpp | 8 ++++++++ srtcore/core.cpp | 6 ++++++ srtcore/queue.cpp | 5 +++++ srtcore/queue.h | 2 ++ 4 files changed, 21 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index aee13f389..ab57fa42b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1964,6 +1964,14 @@ int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); + // This socket might be currently during reading from + // the receiver queue as called from `srt_connect` API. + // Until this procedure breaks, locking s->m_ControlLock + // would have to wait. Mark it closing right now and force + // the receiver queue to stop waiting immediately. + s->m_pUDT->m_bClosing = true; + s->m_pUDT->m_pRcvQueue->kick(); + ScopedLock socket_cg(s->m_ControlLock); HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSING (removing from listening, closing CUDT)"); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 7a337cf41..e42a276f3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3659,6 +3659,12 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // listener should respond with HS_VERSION_SRT1, if it is HSv5 capable. } + // The queue could have been kicked by the close() API call, + // if so, interrupt immediately. + if (m_bClosing || m_bBroken) + break; + + HLOGC(cnlog.Debug, log << "startConnect: timeout from Q:recvfrom, looping again; cst=" << ConnectStatusStr(cst)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index b9d7ed02e..fa135619e 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1758,6 +1758,11 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } +void srt::CRcvQueue::kick() +{ + CSync::lock_broadcast(m_BufferCond, m_BufferLock); +} + void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) { UniqueLock bufferlock(m_BufferLock); diff --git a/srtcore/queue.h b/srtcore/queue.h index ee05440c8..a4460ba5d 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -566,6 +566,8 @@ class CRcvQueue void storePkt(int32_t id, CPacket* pkt); + void kick(); + private: sync::Mutex m_LSLock; CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity From 7164030f4f2f4ff4e5a9adf5f5ac5853a8b13b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Jun 2021 11:14:28 +0200 Subject: [PATCH 034/517] Fixed problem: test if socket is in this blocking-connecting state before changing the flag. Otherwise it would confuse the closing function when used on a connected socket --- srtcore/api.cpp | 42 +++++++++++++++++++++++++++------ test/test_file_transmission.cpp | 9 ++++++- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ab57fa42b..2b77256b5 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1964,13 +1964,41 @@ int srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->m_pUDT->CONID() << " CLOSE. Acquiring control lock"); - // This socket might be currently during reading from - // the receiver queue as called from `srt_connect` API. - // Until this procedure breaks, locking s->m_ControlLock - // would have to wait. Mark it closing right now and force - // the receiver queue to stop waiting immediately. - s->m_pUDT->m_bClosing = true; - s->m_pUDT->m_pRcvQueue->kick(); + // The check for whether m_pRcvQueue isn't NULL is safe enough; + // it can either be NULL after socket creation and without binding + // and then once it's assigned, it's never reset to NULL even when + // destroying the socket. + CUDT* e = s->m_pUDT; + if (e->m_pRcvQueue && e->m_bConnecting && !e->m_bConnected) + { + // Workaround for a design flaw. + // It's to work around the case when the socket is being + // closed in another thread while it's in the process of + // connecting in the blocking mode, that is, it runs the + // loop in `CUDT::startConnect` whole time under the lock + // of CUDT::m_ConnectionLock and CUDTSocket::m_ControlLock + // this way blocking the `srt_close` API call from continuing. + // We are setting here the m_bClosing flag prematurely so + // that the loop may check this flag periodically and exit + // immediately if it's set. + // + // The problem is that this flag shall NOT be set in case + // when you have a CONNECTED socket because not only isn't it + // not a problem in this case, but also it additionally + // turns the socket in a "confused" state in which it skips + // vital part of closing itself and therefore runs an infinite + // loop when trying to purge the sender buffer of the closing + // socket. + // + // XXX Consider refax on CUDT::startConnect and removing the + // connecting loop there and replace the "blocking mode specific" + // connecting procedure with delegation to the receiver queue, + // which will be then common with non-blocking mode, and synchronize + // the blocking through a CV. + + e->m_bClosing = true; + e->m_pRcvQueue->kick(); + } ScopedLock socket_cg(s->m_ControlLock); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 790555eb7..21a920741 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -17,6 +17,7 @@ #endif #include "srt.h" +#include "threadname.h" #include #include @@ -74,6 +75,7 @@ TEST(Transmission, FileUpload) auto client = std::thread([&] { + ThreadName::set("TEST-in"); sockaddr_in remote; int len = sizeof remote; const SRTSOCKET accepted_sock = srt_accept(sock_lsn, (sockaddr*)&remote, &len); @@ -93,7 +95,12 @@ TEST(Transmission, FileUpload) for (;;) { int n = srt_recv(accepted_sock, buf.data(), 1456); - ASSERT_NE(n, SRT_ERROR); + EXPECT_NE(n, SRT_ERROR); + if (n == -1) + { + std::cerr << "UNEXPECTED ERROR: " << srt_getlasterror_str() << std::endl; + break; + } if (n == 0) { break; From cdca85d892a34776e2227d8c3338d780541b631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 2 Jun 2021 11:25:44 +0200 Subject: [PATCH 035/517] Added UT for the closing case --- test/test_common.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/test_common.cpp b/test/test_common.cpp index a4b8c033a..992b93893 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -1,6 +1,11 @@ #include #include +#include + +#include +#include + #include "gtest/gtest.h" #include "utilities.h" #include "common.h" @@ -65,3 +70,49 @@ TEST(CIPAddress, IPv4_in_IPv6_pton) test_cipaddress_pton(peer_ip, AF_INET6, ip); } + + +TEST(SRTAPI, SyncRendezvousHangs) { + ASSERT_EQ(srt_startup(), 0); + + int yes = 1; + + SRTSOCKET m_bindsock = srt_create_socket(); + ASSERT_NE(m_bindsock, SRT_ERROR); + + ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_bindsock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes), 0); + + const int connection_timeout_ms = 1000; // rendezvous timeout is x10 hence 10seconds + ASSERT_EQ(srt_setsockopt(m_bindsock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), 0); + + sockaddr_in local_sa={}; + local_sa.sin_family = AF_INET; + local_sa.sin_port = htons(9999); + local_sa.sin_addr.s_addr = INADDR_ANY; + + sockaddr_in peer_sa= {}; + peer_sa.sin_family = AF_INET; + peer_sa.sin_port = htons(9998); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &peer_sa.sin_addr), 1); + + uint64_t duration = 0; + + std::thread close_thread([&m_bindsock, &duration] { + std::this_thread::sleep_for(std::chrono::seconds(1)); // wait till srt_rendezvous is called + auto start = std::chrono::steady_clock::now(); + srt_close(m_bindsock); + auto end = std::chrono::steady_clock::now(); + + duration = std::chrono::duration_cast(end - start).count(); + }); + + ASSERT_EQ(srt_rendezvous(m_bindsock, (sockaddr*)&local_sa, sizeof local_sa, + (sockaddr*)&peer_sa, sizeof peer_sa), SRT_ERROR); + + close_thread.join(); + ASSERT_LE(duration, 1); + srt_close(m_bindsock); + srt_cleanup(); +} From 7923aae10e274d1c64258b0f71e9e951b11aca7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 25 Aug 2022 10:45:13 +0200 Subject: [PATCH 036/517] Added a UT that confirms the bug. Added an option setter utility. --- apps/apputil.hpp | 37 ++++++++++++++++++++++- test/test_epoll.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index acb28d076..c6e013f02 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -20,6 +20,7 @@ #include "netinet_any.h" #include "utilities.h" +#include "srt.h" #if _WIN32 @@ -332,8 +333,42 @@ inline bool OptionPresent(const options_t& options, const std::set& options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); std::string OptionHelpItem(const OptionName& o); - const char* SRTClockTypeStr(); void PrintLibVersion(); + +namespace srt +{ + +struct OptionSetterProxy +{ + SRTSOCKET s = -1; + int result = 0; + + struct OptionProxy + { + OptionSetterProxy& parent; + SRT_SOCKOPT opt; + + template + OptionProxy& operator=(Type&& val) + { + Type vc(val); + srt_setsockflag(parent.s, opt, &vc, sizeof vc); + return *this; + } + }; + + OptionProxy operator[](SRT_SOCKOPT opt) + { + return OptionProxy {*this, opt}; + } +}; + +inline OptionSetterProxy setopt(SRTSOCKET socket) +{ + return OptionSetterProxy {socket}; +} + +} #endif // INC_SRT_APPCOMMON_H diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index d5e044d7e..497bce692 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -6,7 +6,7 @@ #include "gtest/gtest.h" #include "api.h" #include "epoll.h" - +#include "apputil.hpp" using namespace std; using namespace srt; @@ -561,6 +561,76 @@ TEST(CEPoll, ThreadedUpdate) EXPECT_EQ(srt_cleanup(), 0); } +TEST(CEPoll, LateListenerReady) +{ + int server_sock = srt_create_socket(), caller_sock = srt_create_socket(); + + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, 1); + + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + +// This macro is to manipulate with the moment when the call is made +// towards the eid subscription. If 1, then the call is made first, +// and then subsciption after a 1s time. Set it to 0 to see how it +// works when the subscription is made first, so the readiness is from +// the listener changing the state. +#define LATE_CALL 1 + +#if LATE_CALL + + // We don't need the caller to be async, it can hang up here. + auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + +#endif + this_thread::sleep_for(chrono::milliseconds(1000)); + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + +#if !LATE_CALL + + // We don't need the caller to be async, it can hang up here. + auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + +#endif + + // And see now if the waiting accepted socket reports it. + SRT_EPOLL_EVENT fdset[1]; + EXPECT_EQ(srt_epoll_uwait(eid, fdset, 1, 5000), 1); + + sockaddr_in scl; + int sclen = sizeof scl; + SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + + srt_epoll_release(eid); + srt_close(sock); + srt_close(server_sock); + srt_close(caller_sock); +} + class TestEPoll: public testing::Test { From 6cf64f5944408311fd0930f14dff6dbd87995cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 25 Aug 2022 14:55:51 +0200 Subject: [PATCH 037/517] Fixed clang errors. Added compiler flags to configure to prevent changes by google test --- apps/apputil.hpp | 13 +++++++----- configure-data.tcl | 53 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index c6e013f02..c79cf984e 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -333,6 +333,7 @@ inline bool OptionPresent(const options_t& options, const std::set& options_t ProcessOptions(char* const* argv, int argc, std::vector scheme); std::string OptionHelpItem(const OptionName& o); + const char* SRTClockTypeStr(); void PrintLibVersion(); @@ -342,12 +343,14 @@ namespace srt struct OptionSetterProxy { - SRTSOCKET s = -1; - int result = 0; + SRTSOCKET s; + int result = -1; + + OptionSetterProxy(SRTSOCKET ss): s(ss) {} struct OptionProxy { - OptionSetterProxy& parent; + const OptionSetterProxy& parent; SRT_SOCKOPT opt; template @@ -359,7 +362,7 @@ struct OptionSetterProxy } }; - OptionProxy operator[](SRT_SOCKOPT opt) + OptionProxy operator[](SRT_SOCKOPT opt) const { return OptionProxy {*this, opt}; } @@ -367,7 +370,7 @@ struct OptionSetterProxy inline OptionSetterProxy setopt(SRTSOCKET socket) { - return OptionSetterProxy {socket}; + return OptionSetterProxy(socket); } } diff --git a/configure-data.tcl b/configure-data.tcl index eace99da1..40500a721 100644 --- a/configure-data.tcl +++ b/configure-data.tcl @@ -190,7 +190,24 @@ proc preprocess {} { } } -proc GetCompilerCommand {} { +set compiler_map { + cc c++ + gcc g++ +} + +proc GetCompilerCmdName {compiler lang} { + if {$lang == "c++"} { + if { [dict exists $::compiler_map $compiler] } { + return [dict get $::compiler_map $compiler] + } + + return ${compiler}++ + } + + return $compiler +} + +proc GetCompilerCommand { {lang {}} } { # Expect that the compiler was set through: # --with-compiler-prefix # --cmake-c[++]-compiler @@ -203,21 +220,25 @@ proc GetCompilerCommand {} { if { [info exists ::optval(--with-compiler-prefix)] } { set prefix $::optval(--with-compiler-prefix) - return ${prefix}$compiler + return ${prefix}[GetCompilerCmdName $compiler $lang] } else { - return $compiler + return [GetCompilerCmdName $compiler $lang] } - if { [info exists ::optval(--cmake-c-compiler)] } { - return $::optval(--cmake-c-compiler) + if { $lang != "c++" } { + if { [info exists ::optval(--cmake-c-compiler)] } { + return $::optval(--cmake-c-compiler) + } } - if { [info exists ::optval(--cmake-c++-compiler)] } { - return $::optval(--cmake-c++-compiler) - } + if { $lang != "c" } { + if { [info exists ::optval(--cmake-c++-compiler)] } { + return $::optval(--cmake-c++-compiler) + } - if { [info exists ::optval(--cmake-cxx-compiler)] } { - return $::optval(--cmake-cxx-compiler) + if { [info exists ::optval(--cmake-cxx-compiler)] } { + return $::optval(--cmake-cxx-compiler) + } } puts "NOTE: Cannot obtain compiler, assuming toolchain file will do what's necessary" @@ -283,6 +304,18 @@ proc postprocess {} { } else { puts "CONFIGURE: default compiler used" } + + # Complete the variables before calling cmake, otherwise it might not work + + if { [info exists ::optval(--with-compiler-type)] } { + if { ![info exists ::optval(--cmake-c-compiler)] } { + lappend ::cmakeopt "-DCMAKE_C_COMPILER=[GetCompilerCommand c]" + } + + if { ![info exists ::optval(--cmake-c++-compiler)] } { + lappend ::cmakeopt "-DCMAKE_CXX_COMPILER=[GetCompilerCommand c++]" + } + } } if { $::srt_name != "" } { From 068e8a9b3de03296be6222b8788dd53ed8f62542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 26 Aug 2022 13:27:19 +0200 Subject: [PATCH 038/517] Added checking of accept candidates. In-group check disabled and left dead code (this solution deadlocks) --- srtcore/api.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/api.h | 4 ++++ srtcore/core.cpp | 20 ++++++++++++++++ srtcore/group.h | 5 ++++ test/test_epoll.cpp | 4 ++++ 5 files changed, 91 insertions(+) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index d575cd968..642067a3f 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -835,6 +835,64 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return 1; } +SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() +{ + // You need to check EVERY socket that has been queued + // and verify its internals. With independent socket the + // matter is simple - if it's present, you light up the + // SRT_EPOLL_ACCEPT flag. + +//#if !ENABLE_BONDING +#if 1 + ScopedLock accept_lock (m_AcceptLock); + + // Make it simplified here - nonempty container = have acceptable sockets. + // Might make sometimes spurious acceptance, but this can also happen when + // the incoming accepted socket was suddenly broken. + return m_QueuedSockets.empty() ? 0 : int(SRT_EPOLL_ACCEPT); + +#else // Could do #endif here, but the compiler would complain about unreachable code. + + set sockets_copy; + { + ScopedLock accept_lock (m_AcceptLock); + sockets_copy = m_QueuedSockets; + } + return CUDT::uglobal().checkQueuedSocketsEvents(sockets_copy); + +#endif +} + +#if ENABLE_BONDING +int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) +{ + SRT_EPOLL_T flags = 0; + + // But with the member sockets an appropriate check must be + // done first: if this socket belongs to a group that is + // already in the connected state, you should light up the + // SRT_EPOLL_UPDATE flag instead. This flag is only for + // internal informing the waiters on the listening sockets + // that they should re-read the group list and re-check readiness. + + // Now we can do lock once and for all + //ScopedLock glk (m_GlobControlLock); // XXX DEADLOCKS + + for (set::iterator i = sockets.begin(); i != sockets.end(); ++i) + { + CUDTSocket* s = locateSocket_LOCKED(*i); + if (!s) + continue; // wiped in the meantime - ignore + if (s->m_GroupOf && s->m_GroupOf->groupConnected()) + flags |= SRT_EPOLL_UPDATE; + else + flags |= SRT_EPOLL_ACCEPT; + } + + return flags; +} +#endif + // static forwarder int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { diff --git a/srtcore/api.h b/srtcore/api.h index ca812bf9e..6254812a4 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -158,6 +158,8 @@ class CUDTSocket unsigned int m_uiBackLog; //< maximum number of connections in queue + SRT_EPOLL_T getListenerEvents(); + // XXX A refactoring might be needed here. // There are no reasons found why the socket can't contain a list iterator to a @@ -264,6 +266,8 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); + int checkQueuedSocketsEvents(const std::set& sockets); + int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 93224f418..18b9c013c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11515,6 +11515,26 @@ void srt::CUDT::addEPoll(const int eid) m_sPollID.insert(eid); leaveCS(uglobal().m_EPoll.m_EPollLock); + //* + if (m_bListening) + { + // A listener socket can only get readiness on SRT_EPOLL_ACCEPT + // (which has the same value as SRT_EPOLL_IN), or sometimes + // also SRT_EPOLL_UPDATE. All interesting fields for that purpose + // are contained in the CUDTSocket class, so redirect there. + SRT_EPOLL_T events = m_parent->getListenerEvents(); + + // Only light up the events that were returned, do nothing if none is ready, + // the "no event" state is the default. + if (events) + uglobal().m_EPoll.update_events(m_SocketID, m_sPollID, events, true); + + // You don't check anything else here - a listener socket can be only + // used for listening and nothing else. + return; + } + // */ + if (!stillConnected()) return; diff --git a/srtcore/group.h b/srtcore/group.h index 1bd84aeda..c5f147f23 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -206,6 +206,11 @@ class CUDTGroup return m_Group.empty(); } + bool groupConnected() + { + return m_bConnected; + } + void setGroupConnected(); int send(const char* buf, int len, SRT_MSGCTRL& w_mc); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 497bce692..47b488b88 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -563,6 +563,8 @@ TEST(CEPoll, ThreadedUpdate) TEST(CEPoll, LateListenerReady) { + ASSERT_EQ(srt_startup(), 0); + int server_sock = srt_create_socket(), caller_sock = srt_create_socket(); sockaddr_in sa; @@ -629,6 +631,8 @@ TEST(CEPoll, LateListenerReady) srt_close(sock); srt_close(server_sock); srt_close(caller_sock); + + EXPECT_EQ(srt_cleanup(), 0); } From 496e6a74338e8ac16fad261aeaa5343d3e16f4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Sep 2022 17:25:14 +0200 Subject: [PATCH 039/517] Exported fragment to a separate function --- srtcore/core.cpp | 517 +++++++++++++++++++++++++++-------------------- srtcore/core.h | 6 + 2 files changed, 303 insertions(+), 220 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 93224f418..1972d57c5 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9829,20 +9829,10 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return true; } -int srt::CUDT::processData(CUnit* in_unit) +int srt::CUDT::checkLazySpawnLatencyThread() { - if (m_bClosing) - return -1; - - CPacket &packet = in_unit->m_Packet; - - // Just heard from the peer, reset the expiration count. - m_iEXPCount = 1; - m_tsLastRspTime.store(steady_clock::now()); - const bool need_tsbpd = m_bTsbPd || m_bGroupTsbPd; - // We are receiving data, start tsbpd thread if TsbPd is enabled if (need_tsbpd && !m_RcvTsbPdThread.joinable()) { ScopedLock lock(m_RcvTsbPdStartupLock); @@ -9869,9 +9859,264 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + return 0; +} + +CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +{ + return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); +} + +static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; + + +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +{ + int initial_loss_ttl = 0; + if (m_bPeerRexmitFlag) + initial_loss_ttl = m_iReorderTolerance; + + bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added + w_reorder_prevent_lossreport = false; + + // Loop over all incoming packets that were filtered out. + // In case when there is no filter, there's just one packet in 'incoming', + // the one that came in the input of this function. + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + { + CUnit * u = *unitIt; + CPacket &rpkt = u->m_Packet; + const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; + + time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); + IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + + int buffer_add_result; + bool adding_successful = true; + + // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. + // This is the offset in the buffer; if this is negative, it means that + // this sequence is already in the past and the buffer is not interested. + // Meaning, this packet will be rejected, even if it could potentially be + // one of missing packets in the transmission. + int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); + + IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); + + if (offset < 0) + { + IF_HEAVY_LOGGING(exc_type = "BELATED"); + enterCS(m_StatsLock); + const double bltime = (double) CountIIR( + uint64_t(m_stats.traceBelatedTime) * 1000, + count_microseconds(steady_clock::now() - pts), 0.2); + + m_stats.traceBelatedTime = bltime / 1000.0; + m_stats.rcvr.recvdBelated.count(rpkt.getLength()); + leaveCS(m_StatsLock); + HLOGC(qrlog.Debug, + log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo << " offset=" << offset << " (BELATED/" + << rexmitstat[pktrexmitflag] << ") FLAGS: " << rpkt.MessageFlagStr()); + continue; + } + + int avail_bufsize = 0; // needed in logging + + // This is executed only when bonding is enabled and only + // with the new buffer (in which case the buffer is in the group). + + avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); + if (offset >= avail_bufsize) + { + // This is already a sequence discrepancy. Probably there could be found + // some way to make it continue reception by overriding the sequence and + // make a kinda TLKPTDROP, but there has been found no reliable way to do this. + if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) + { + // Only in live mode. In File mode this shall not be possible + // because the sender should stop sending in this situation. + // In Live mode this means that there is a gap between the + // lowest sequence in the empty buffer and the incoming sequence + // that exceeds the buffer size. Receiving data in this situation + // is no longer possible and this is a point of no return. + + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + + return -2; + } + else + { +#if ENABLE_NEW_RCVBUFFER + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) + ); +#else + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + << ", insert offset " << offset << ". " + << m_pRcvBuffer->strFullnessState(steady_clock::now()) + ); +#endif + + return -1; + } + } + +#if ENABLE_NEW_RCVBUFFER + buffer_add_result = m_pRcvBuffer->insert(u); +#else + buffer_add_result = m_pRcvBuffer->addData(u, offset); +#endif + if (buffer_add_result < 0) + { + // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. + // So this packet is "redundant". + IF_HEAVY_LOGGING(exc_type = "UNACKED"); + adding_successful = false; + } + else + { + w_new_inserted = true; + + IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); + excessive = false; + if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) + { + EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + if (rc != ENCS_CLEAR) + { + // Heavy log message because if seen once the message may happen very often. + HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); + adding_successful = false; + IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + + ScopedLock lg(m_StatsLock); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + } + } + } + + if (adding_successful) + { + ScopedLock statslock(m_StatsLock); + m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); + } + +#if ENABLE_HEAVY_LOGGING + std::ostringstream expectspec; + if (excessive) + expectspec << "EXCESSIVE(" << exc_type << ")"; + else + expectspec << "ACCEPTED"; + + std::ostringstream bufinfo; + + if (m_pRcvBuffer) + { + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) + << ")"; + } + + // Empty buffer info in case of groupwise receiver. + // There's no way to obtain this information here. + + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo + << " offset=" << offset + << bufinfo.str() + << " RSL=" << expectspec.str() + << " SN=" << rexmitstat[pktrexmitflag] + << " FLAGS: " + << rpkt.MessageFlagStr()); +#endif + + // Decryption should have made the crypto flags EK_NOENC. + // Otherwise it's an error. + if (adding_successful) + { + HLOGC(qrlog.Debug, + log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + + // Lame way to make an optional variable. + bool have_loss = false; + + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + { + int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + + have_loss = true; + w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); + HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); + } + + if (initial_loss_ttl && have_loss) + { + // pack loss list for (possibly belated) NAK + // The LOSSREPORT will be sent in a while. + + ScopedLock lg(m_RcvLossLock); + for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) + { + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + HLOGC(qrlog.Debug, + log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second + << ") tolerance: " << initial_loss_ttl); + + } + w_reorder_prevent_lossreport = true; + } + } + + // Update the current largest sequence number that has been received. + // Or it is a retransmitted packet, remove it from receiver loss list. + // + // Group note: for the new group receiver the group hosts the receiver + // buffer, but the socket still maintains the losses. + if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + { + m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + } + else + { + unlose(rpkt); // was BELATED or RETRANSMITTED + w_was_sent_in_order &= 0 != pktrexmitflag; + } + } + + return 0; +} + +int srt::CUDT::processData(CUnit* in_unit) +{ + if (m_bClosing) + return -1; + + CPacket &packet = in_unit->m_Packet; + + // Just heard from the peer, reset the expiration count. + m_iEXPCount = 1; + m_tsLastRspTime.store(steady_clock::now()); + + + // We are receiving data, start tsbpd thread if TsbPd is enabled + if (-1 == checkLazySpawnLatencyThread()) + { + return -1; + } + const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2; #if ENABLE_HEAVY_LOGGING - static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; string rexmit_reason; #endif @@ -10032,223 +10277,55 @@ int srt::CUDT::processData(CUnit* in_unit) } #endif + // NULL time by default + time_point next_tsbpd_avail; + bool new_inserted = false; + + if (m_PacketFilter) + { + // Stuff this data into the filter + m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); + HLOGC(qrlog.Debug, + log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + << " loss to report, " + << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" + : "REPORT ONLY THOSE")); + } + else + { + // Stuff in just one packet that has come in. + incoming.push_back(in_unit); + } + { // Start of offset protected section // Prevent TsbPd thread from modifying Ack position while adding data // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); - - // vector undec_units; - if (m_PacketFilter) - { - // Stuff this data into the filter - m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); - HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) - << " loss to report, " - << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" - : "REPORT ONLY THOSE")); - } - else - { - // Stuff in just one packet that has come in. - incoming.push_back(in_unit); - } - - bool excessive = true; // stays true unless it was successfully added - // Needed for possibly check for needsQuickACK. bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_iRcvLastSkipAck) < 0); - // Loop over all incoming packets that were filtered out. - // In case when there is no filter, there's just one packet in 'incoming', - // the one that came in the input of this function. - for (vector::iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) - { - CUnit * u = *unitIt; - CPacket &rpkt = u->m_Packet; - - // m_iRcvLastSkipAck is the base sequence number for the receiver buffer. - // This is the offset in the buffer; if this is negative, it means that - // this sequence is already in the past and the buffer is not interested. - // Meaning, this packet will be rejected, even if it could potentially be - // one of missing packets in the transmission. - int32_t offset = CSeqNo::seqoff(m_iRcvLastSkipAck, rpkt.m_iSeqNo); - - IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); - - if (offset < 0) - { - IF_HEAVY_LOGGING(exc_type = "BELATED"); - steady_clock::time_point tsbpdtime = m_pRcvBuffer->getPktTsbPdTime(rpkt.getMsgTimeStamp()); - const double bltime = (double) CountIIR( - uint64_t(m_stats.traceBelatedTime) * 1000, - count_microseconds(steady_clock::now() - tsbpdtime), 0.2); - - enterCS(m_StatsLock); - m_stats.traceBelatedTime = bltime / 1000.0; - m_stats.rcvr.recvdBelated.count(rpkt.getLength()); - leaveCS(m_StatsLock); - HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: seq=" << packet.m_iSeqNo << " offset=" << offset << " (BELATED/" - << rexmitstat[pktrexmitflag] << rexmit_reason << ") FLAGS: " << packet.MessageFlagStr()); - continue; - } - - const int avail_bufsize = (int) getAvailRcvBufferSizeNoLock(); - if (offset >= avail_bufsize) - { - // This is already a sequence discrepancy. Probably there could be found - // some way to make it continue reception by overriding the sequence and - // make a kinda TLKPTDROP, but there has been found no reliable way to do this. - if (m_bTsbPd && m_bTLPktDrop && m_pRcvBuffer->empty()) - { - // Only in live mode. In File mode this shall not be possible - // because the sender should stop sending in this situation. - // In Live mode this means that there is a gap between the - // lowest sequence in the empty buffer and the incoming sequence - // that exceeds the buffer size. Receiving data in this situation - // is no longer possible and this is a point of no return. - - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); - - // This is a scoped lock with AckLock, but for the moment - // when processClose() is called this lock must be taken out, - // otherwise this will cause a deadlock. We don't need this - // lock anymore, and at 'return' it will be unlocked anyway. - recvbuf_acklock.unlock(); - processClose(); - return -1; - } - else - { -#if ENABLE_NEW_RCVBUFFER - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) - ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif - - return -1; - } - } + int res = handleSocketPacketReception(incoming, + (new_inserted), + (was_sent_in_order), + (reorder_prevent_lossreport), + (srt_loss_seqs)); - bool adding_successful = true; -#if ENABLE_NEW_RCVBUFFER - if (m_pRcvBuffer->insert(u) < 0) -#else - if (m_pRcvBuffer->addData(u, offset) < 0) -#endif - { - // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. - // So this packet is "redundant". - IF_HEAVY_LOGGING(exc_type = "UNACKED"); - adding_successful = false; - } - else - { - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); - excessive = false; - if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) - { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; - if (rc != ENCS_CLEAR) - { - // Heavy log message because if seen once the message may happen very often. - HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data."); - adding_successful = false; - IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); - - ScopedLock lg(m_StatsLock); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1)); - } - } - } - - if (adding_successful) - { - ScopedLock statslock(m_StatsLock); - m_stats.rcvr.recvdUnique.count(u->m_Packet.getLength()); - } - -#if ENABLE_HEAVY_LOGGING - std::ostringstream expectspec; - if (excessive) - expectspec << "EXCESSIVE(" << exc_type << rexmit_reason << ")"; - else - expectspec << "ACCEPTED"; - - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: seq=" << rpkt.m_iSeqNo - << " offset=" << offset - << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) - << ") " - << " RSL=" << expectspec.str() - << " SN=" << rexmitstat[pktrexmitflag] - << " FLAGS: " - << rpkt.MessageFlagStr()); -#endif - - // Decryption should have made the crypto flags EK_NOENC. - // Otherwise it's an error. - if (adding_successful) - { - // XXX move this code do CUDT::defaultPacketArrival and call it from here: - // srt_loss_seqs = CALLBACK_CALL(m_cbPacketArrival, rpkt); - - HLOGC(qrlog.Debug, - log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. - { - int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - - srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. + if (res == -2) + { + // This is a scoped lock with AckLock, but for the moment + // when processClose() is called this lock must be taken out, + // otherwise this will cause a deadlock. We don't need this + // lock anymore, and at 'return' it will be unlocked anyway. + recvbuf_acklock.unlock(); + processClose(); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - } - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: " << Printable(srt_loss_seqs) - << " tolerance: " << initial_loss_ttl); - reorder_prevent_lossreport = true; - } - } - } + return -1; + } - // Update the current largest sequence number that has been received. - // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) - { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received - } - else - { - unlose(rpkt); // was BELATED or RETRANSMITTED - was_sent_in_order &= 0 != pktrexmitflag; - } + if (res == -1) + { + return -1; } // This is moved earlier after introducing filter because it shouldn't @@ -10269,11 +10346,11 @@ int srt::CUDT::processData(CUnit* in_unit) } } - if (excessive) + if (!new_inserted) { return -1; } - } // End of recvbuf_acklock + } if (m_bClosing) { diff --git a/srtcore/core.h b/srtcore/core.h index fa58ca7c2..72946a5bf 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,6 +1062,12 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + + // Group passed here by void* because in the current imp it's + // unused and shall not be used in case when bonding is off + time_point getPacketPTS(void* grp, const CPacket& packet); + int checkLazySpawnLatencyThread(); void processClose(); /// Process the request after receiving the handshake from caller. From 03214ff8bf36706e0378335af8288211fcd92fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 30 Sep 2022 18:03:29 +0200 Subject: [PATCH 040/517] Applied changes from PR 2467-before merging --- srtcore/core.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1972d57c5..4c50d30cf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9940,14 +9940,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, log << CONID() << - "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastSkipAck - << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) - << "), " << (offset-avail_bufsize+1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, + log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " + << (offset - avail_bufsize + 1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } @@ -10020,8 +10018,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo << " BUFr=" << avail_bufsize - << " avail=" << getAvailRcvBufferSizeNoLock() + bufinfo + << " avail=" << avail_bufsize << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) From 96e92dd558285ed484d69e2bbc611b2ff401fc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 25 Oct 2022 15:51:32 +0200 Subject: [PATCH 041/517] Adjusted to the latest changes in the group branch, per changes in master --- srtcore/core.cpp | 192 +++++++++++++++++++---------------------------- srtcore/core.h | 2 +- 2 files changed, 77 insertions(+), 117 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 4c50d30cf..1355daf01 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9870,14 +9870,9 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_lossreport, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { - int initial_loss_ttl = 0; - if (m_bPeerRexmitFlag) - initial_loss_ttl = m_iReorderTolerance; - bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added - w_reorder_prevent_lossreport = false; // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', @@ -9940,38 +9935,29 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // that exceeds the buffer size. Receiving data in this situation // is no longer possible and this is a point of no return. - LOGC(qrlog.Error, - log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION. seq=" << rpkt.m_iSeqNo - << " buffer=(" << m_iRcvLastAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index - << "+" << CSeqNo::incseq(m_iRcvLastAck, int(m_pRcvBuffer->capacity()) - 1) << "), " - << (offset - avail_bufsize + 1) - << " past max. Reception no longer possible. REQUESTING TO CLOSE."); + LOGC(qrlog.Error, log << CONID() << + "SEQUENCE DISCREPANCY. BREAKING CONNECTION." + " seq=" << rpkt.m_iSeqNo + << " buffer=(" << m_iRcvLastSkipAck + << ":" << m_iRcvCurrSeqNo // -1 = size to last index + << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, int(m_pRcvBuffer->capacity()) - 1) + << "), " << (offset-avail_bufsize+1) + << " past max. Reception no longer possible. REQUESTING TO CLOSE."); return -2; } else { -#if ENABLE_NEW_RCVBUFFER LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo << ", insert offset " << offset << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); -#else - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo - << ", insert offset " << offset << ". " - << m_pRcvBuffer->strFullnessState(steady_clock::now()) - ); -#endif return -1; } } -#if ENABLE_NEW_RCVBUFFER buffer_add_result = m_pRcvBuffer->insert(u); -#else - buffer_add_result = m_pRcvBuffer->addData(u, offset); -#endif if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -10018,8 +10004,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (m_pRcvBuffer) { - bufinfo - << " avail=" << avail_bufsize + bufinfo << " BUFr=" << avail_bufsize + << " avail=" << getAvailRcvBufferSizeNoLock() << " buffer=(" << m_iRcvLastSkipAck << ":" << m_iRcvCurrSeqNo // -1 = size to last index << "+" << CSeqNo::incseq(m_iRcvLastSkipAck, m_pRcvBuffer->capacity()-1) @@ -10045,35 +10031,15 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& HLOGC(qrlog.Debug, log << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); - // Lame way to make an optional variable. - bool have_loss = false; - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); - have_loss = true; w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } - if (initial_loss_ttl && have_loss) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = w_srt_loss_seqs.begin(); i != w_srt_loss_seqs.end(); ++i) - { - m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); - HLOGC(qrlog.Debug, - log << "FreshLoss: added sequences: %(" << i->first << "-" << i->second - << ") tolerance: " << initial_loss_ttl); - - } - w_reorder_prevent_lossreport = true; - } } // Update the current largest sequence number that has been received. @@ -10189,7 +10155,6 @@ int srt::CUDT::processData(CUnit* in_unit) loss_seqs_t srt_loss_seqs; vector incoming; bool was_sent_in_order = true; - bool reorder_prevent_lossreport = false; // If the peer doesn't understand REXMIT flag, send rexmit request // always immediately. @@ -10197,37 +10162,26 @@ int srt::CUDT::processData(CUnit* in_unit) if (m_bPeerRexmitFlag) initial_loss_ttl = m_iReorderTolerance; - // After introduction of packet filtering, the "recordable loss detection" - // does not exactly match the true loss detection. When a FEC filter is - // working, for example, then getting one group filled with all packet but - // the last one and the FEC control packet, in this special case this packet - // won't be notified at all as lost because it will be recovered by the - // filter immediately before anyone notices what happened (and the loss - // detection for the further functionality is checked only afterwards, - // and in this case the immediate recovery makes the loss to not be noticed - // at all). - // - // Because of that the check for losses must happen BEFORE passing the packet - // to the filter and before the filter could recover the packet before anyone - // notices :) - - if (packet.getMsgSeq() != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing - { - int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); - // Difference between these two sequence numbers is expected to be: - // 0 - duplicated last packet (theory only) - // 1 - subsequent packet (alright) - // <0 - belated or recovered packet - // >1 - jump over a packet loss (loss = seqdiff-1) + // Track packet loss in statistics early, because a packet filter (e.g. FEC) might recover it later on, + // supply the missing packet(s), and the loss will no longer be visible for the code that follows. + if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing + { + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + // Difference between these two sequence numbers is expected to be: + // 0 - duplicated last packet (theory only) + // 1 - subsequent packet (alright) + // <0 - belated or recovered packet + // >1 - jump over a packet loss (loss = seqdiff-1) if (diff > 1) { const int loss = diff - 1; // loss is all that is above diff == 1 + ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, - log << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " + log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " << CSeqNo::decseq(packet.m_iSeqNo) << "]"); } @@ -10256,20 +10210,21 @@ int srt::CUDT::processData(CUnit* in_unit) // This check is needed as after getting the lock the socket // could be potentially removed. It is however granted that as long // as gi is non-NULL iterator, the group does exist and it does contain - // this socket as member (that is, 'gi' cannot be a dangling iterator). + // this socket as member (that is, 'gi' cannot be a dangling pointer). if (gi != NULL) { if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely { HLOGC(qrlog.Debug, - log << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] + log << CONID() << "processData: IN-GROUP rcv state transition " << srt_log_grp_state[gi->rcvstate] << " -> RUNNING."); gi->rcvstate = SRT_GST_RUNNING; } else { - HLOGC(qrlog.Debug, log << "processData: IN-GROUP rcv state transition NOT DONE - state:" - << srt_log_grp_state[gi->rcvstate]); + HLOGC(qrlog.Debug, + log << CONID() << "processData: IN-GROUP rcv state transition NOT DONE - state:" + << srt_log_grp_state[gi->rcvstate]); } } } @@ -10284,7 +10239,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Stuff this data into the filter m_PacketFilter.receive(in_unit, (incoming), (filter_loss_seqs)); HLOGC(qrlog.Debug, - log << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) + log << CONID() << "(FILTER) fed data, received " << incoming.size() << " pkts, " << Printable(filter_loss_seqs) << " loss to report, " << (m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS ? "FIND & REPORT LOSSES YOURSELF" : "REPORT ONLY THOSE")); @@ -10306,7 +10261,6 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), (was_sent_in_order), - (reorder_prevent_lossreport), (srt_loss_seqs)); if (res == -2) @@ -10326,6 +10280,25 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + if (!srt_loss_seqs.empty()) + { + ScopedLock lock(m_RcvLossLock); + + HLOGC(qrlog.Debug, + log << CONID() << "processData: RECORDING LOSS: " << Printable(srt_loss_seqs) + << " tolerance=" << initial_loss_ttl); + + for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) + { + m_pRcvLossList->insert(i->first, i->second); + if (initial_loss_ttl) + { + // The LOSSREPORT will be sent after initial_loss_ttl. + m_FreshLoss.push_back(CRcvFreshLoss(i->first, i->second, initial_loss_ttl)); + } + } + } + // This is moved earlier after introducing filter because it shouldn't // be executed in case when the packet was rejected by the receiver buffer. // However now the 'excessive' condition may be true also in case when @@ -10370,37 +10343,21 @@ int srt::CUDT::processData(CUnit* in_unit) if (!srt_loss_seqs.empty()) { - // A loss is detected - { - // TODO: Can unlock rcvloss after m_pRcvLossList->insert(...)? - // And probably protect m_FreshLoss as well. - - HLOGC(qrlog.Debug, log << "processData: LOSS DETECTED, %: " << Printable(srt_loss_seqs) << " - RECORDING."); - // if record_loss == false, nothing will be contained here - // Insert lost sequence numbers to the receiver loss list - ScopedLock lg(m_RcvLossLock); - for (loss_seqs_t::iterator i = srt_loss_seqs.begin(); i != srt_loss_seqs.end(); ++i) - { - // If loss found, insert them to the receiver loss list - m_pRcvLossList->insert(i->first, i->second); - } - } - const bool report_recorded_loss = !m_PacketFilter || m_PktFilterRexmitLevel == SRT_ARQ_ALWAYS; - if (!reorder_prevent_lossreport && report_recorded_loss) + if (!initial_loss_ttl && report_recorded_loss) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } else { - HLOGC(qrlog.Debug, log << "loss: socket is not TSBPD, not signaling"); + HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } } @@ -10411,12 +10368,12 @@ int srt::CUDT::processData(CUnit* in_unit) // With NEVER, nothing is to be reported. if (!filter_loss_seqs.empty()) { - HLOGC(qrlog.Debug, log << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); + HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); if (m_bTsbPd) { - HLOGC(qrlog.Debug, log << "loss: signaling TSBPD cond"); + HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); } } @@ -10781,7 +10738,7 @@ void srt::CUDT::dropFromLossLists(int32_t from, int32_t to) ScopedLock lg(m_RcvLossLock); m_pRcvLossList->remove(from, to); - HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqoff(from, to)); + HLOGF(qrlog.Debug, "%sTLPKTDROP seq %d-%d (%d packets)", CONID().c_str(), from, to, CSeqNo::seqlen(from, to)); if (m_bPeerRexmitFlag == 0 || m_iReorderTolerance == 0) return; @@ -10883,12 +10840,12 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // XXX ASSUMPTIONS: // [[using assert(packet.m_iID == 0)]] - HLOGC(cnlog.Debug, log << "processConnectRequest: received a connection request"); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); if (m_bClosing) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because closing."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because closing."); return m_RejectReason; } @@ -10900,7 +10857,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (m_bBroken) { m_RejectReason = SRT_REJ_CLOSE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ... NOT. Rejecting because broken."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... NOT. Rejecting because broken."); return m_RejectReason; } // When CHandShake::m_iContentSize is used in log, the file fails to link! @@ -10916,8 +10873,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { m_RejectReason = SRT_REJ_ROGUE; HLOGC(cnlog.Debug, - log << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() << " (expected: " << exp_len - << ")"); + log << CONID() << "processConnectRequest: ... NOT. Wrong size: " << packet.getLength() + << " (expected: " << exp_len << ")"); return m_RejectReason; } @@ -10927,7 +10884,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!packet.isControl(UMSG_HANDSHAKE)) { m_RejectReason = SRT_REJ_ROGUE; - LOGC(cnlog.Error, log << "processConnectRequest: the packet received as handshake is not a handshake message"); + LOGC(cnlog.Error, + log << CONID() << "processConnectRequest: the packet received as handshake is not a handshake message"); return m_RejectReason; } @@ -10945,14 +10903,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) int32_t cookie_val = bake(addr); - HLOGC(cnlog.Debug, log << "processConnectRequest: new cookie: " << hex << cookie_val); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val); // REQUEST:INDUCTION. // Set a cookie, a target ID, and send back the same as // RESPONSE:INDUCTION. if (hs.m_iReqType == URQ_INDUCTION) { - HLOGC(cnlog.Debug, log << "processConnectRequest: received type=induction, sending back with cookie+socket"); + HLOGC(cnlog.Debug, + log << CONID() << "processConnectRequest: received type=induction, sending back with cookie+socket"); // XXX That looks weird - the calculated md5 sum out of the given host/port/timestamp // is 16 bytes long, but CHandShake::m_iCookie has 4 bytes. This then effectively copies @@ -10981,7 +10940,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iType = SrtHSRequest::wrapFlags(true /*put SRT_MAGIC_CODE in HSFLAGS*/, m_config.iSndCryptoKeyLen); bool whether SRT_ATR_UNUSED = m_config.iSndCryptoKeyLen != 0; HLOGC(cnlog.Debug, - log << "processConnectRequest: " << (whether ? "" : "NOT ") + log << CONID() << "processConnectRequest: " << (whether ? "" : "NOT ") << " Advertising PBKEYLEN - value = " << m_config.iSndCryptoKeyLen); size_t size = packet.getLength(); @@ -10989,7 +10948,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) setPacketTS(packet, steady_clock::now()); // Display the HS before sending it to peer - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (i): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (i): " << hs.show()); m_pSndQueue->sendto(addr, packet); return SRT_REJ_UNKNOWN; // EXCEPTION: this is a "no-error" code. @@ -11002,13 +10961,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!hs.valid()) { - LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); + LOGC(cnlog.Error, log << CONID() << "processConnectRequest: ROGUE HS RECEIVED. Rejecting"); m_RejectReason = SRT_REJ_ROGUE; return SRT_REJ_ROGUE; } HLOGC(cnlog.Debug, - log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie..."); + log << CONID() << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) + << " - checking cookie..."); if (hs.m_iCookie != cookie_val) { cookie_val = bake(addr, cookie_val, -1); // SHOULD generate an earlier, distracted cookie @@ -11016,15 +10976,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iCookie != cookie_val) { m_RejectReason = SRT_REJ_RDVCOOKIE; - HLOGC(cnlog.Debug, log << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); return m_RejectReason; } - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (FIXED) cookie. Proceeding."); } else { - HLOGC(cnlog.Debug, log << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } int32_t id = hs.m_iID; @@ -11069,15 +11029,15 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason - << " MSG: " << srt_rejectreason_str(m_RejectReason)); + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.m_iID = id; setPacketTS(packet, steady_clock::now()); - HLOGC(cnlog.Debug, log << "processConnectRequest: SENDING HS (e): " << hs.show()); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } else diff --git a/srtcore/core.h b/srtcore/core.h index 72946a5bf..da113f93f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1062,7 +1062,7 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, bool& w_reorder_prevent_loss, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // Group passed here by void* because in the current imp it's // unused and shall not be used in case when bonding is off From 89e927f99daa1371289c77bd835bfc0c8c5726bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 26 Oct 2022 17:17:58 +0200 Subject: [PATCH 042/517] Added lacking fix for AEAD --- srtcore/core.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 72fee0e45..048d3f903 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9817,7 +9817,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) { - EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + // TODO: reset and restore the timestamp if TSBPD is disabled. + // Reset retransmission flag (must be excluded from GCM auth tag). + u->m_Packet.setRexmitFlag(false); + const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP; + u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag. + if (rc != ENCS_CLEAR) { // Heavy log message because if seen once the message may happen very often. @@ -9825,6 +9830,21 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& adding_successful = false; IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED"); + if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + // Drop a packet from the receiver buffer. + // Dropping depends on the configuration mode. If message mode is enabled, we have to drop the whole message. + // Otherwise just drop the exact packet. + if (m_config.bMessageAPI) + m_pRcvBuffer->dropMessage(SRT_SEQNO_NONE, SRT_SEQNO_NONE, u->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); + else + m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE); + + LOGC(qrlog.Error, log << CONID() << "AEAD decryption failed, breaking the connection."); + m_bBroken = true; + m_iBrokenCounter = 0; + } + ScopedLock lg(m_StatsLock); m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); } From a56c84d5b98e3b3e96d1aea6b4d3623bab997abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 27 Oct 2022 16:05:29 +0200 Subject: [PATCH 043/517] Fixed: block static variable that might be unused when no logging (travis report) --- srtcore/core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 048d3f903..42ac4aab9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9711,8 +9711,7 @@ CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } -static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; - +SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { From 0168c5ea583ffd2963bac90e91cbfcb97390f297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2022 15:06:33 +0100 Subject: [PATCH 044/517] Fixed after PR comments --- srtcore/core.cpp | 4 ++-- srtcore/core.h | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 42ac4aab9..6d12656fd 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPTS(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9728,7 +9728,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const bool retransmitted = pktrexmitflag == 1; time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPTS(NULL, rpkt)); + IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); int buffer_add_result; bool adding_successful = true; diff --git a/srtcore/core.h b/srtcore/core.h index 93674cb3b..8cbf47650 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1053,11 +1053,29 @@ class CUDT std::pair packData(CPacket& packet); int processData(CUnit* unit); + + /// This function passes the incoming packet to the initial processing + /// (like packet filter) and is about to store it effectively to the + /// receiver buffer and do some postprocessing (decryption) if necessary + /// and report the status thereof. + /// + /// @param incoming [in] The packet coming from the network medium + /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. + /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. + /// + /// @return 0 The call was successful (regardless if the packet was accepted or not). + /// @return -1 The call has failed: no space left in the buffer. + /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - // Group passed here by void* because in the current imp it's - // unused and shall not be used in case when bonding is off - time_point getPacketPTS(void* grp, const CPacket& packet); + // This function is to return the packet's play time (time when + // it is submitted to the reading application) of the given packet. + // This grp passed here by void* because in the current imp it's + // unused and shall not be used in case when ENABLE_BONDING=0. + time_point getPacketPlayTime(void* grp, const CPacket& packet); + + /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); void processClose(); From 870509f7e2b1a5ff72eb4520912ce25058d8ddf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 3 Nov 2022 12:08:19 +0100 Subject: [PATCH 045/517] Renamed getPacketPTS. Moved call to obtain PTS under belated condition --- srtcore/core.cpp | 7 +++---- srtcore/core.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 6d12656fd..37a13dc94 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9706,7 +9706,7 @@ int srt::CUDT::checkLazySpawnLatencyThread() return 0; } -CUDT::time_point srt::CUDT::getPacketPlayTime(void*, const CPacket& packet) +CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) { return m_pRcvBuffer->getPktTsbPdTime(packet.getMsgTimeStamp()); } @@ -9727,9 +9727,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const int pktrexmitflag = m_bPeerRexmitFlag ? (rpkt.getRexmitFlag() ? 1 : 0) : 2; const bool retransmitted = pktrexmitflag == 1; - time_point pts = steady_clock::now() + milliseconds_from(m_iTsbPdDelay_ms); - IF_HEAVY_LOGGING(pts = getPacketPlayTime(NULL, rpkt)); - int buffer_add_result; bool adding_successful = true; @@ -9745,6 +9742,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& if (offset < 0) { IF_HEAVY_LOGGING(exc_type = "BELATED"); + time_point pts = getPktTsbPdTime(NULL, rpkt); + enterCS(m_StatsLock); const double bltime = (double) CountIIR( uint64_t(m_stats.traceBelatedTime) * 1000, diff --git a/srtcore/core.h b/srtcore/core.h index 8cbf47650..81979bb83 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1073,7 +1073,7 @@ class CUDT // it is submitted to the reading application) of the given packet. // This grp passed here by void* because in the current imp it's // unused and shall not be used in case when ENABLE_BONDING=0. - time_point getPacketPlayTime(void* grp, const CPacket& packet); + time_point getPktTsbPdTime(void* grp, const CPacket& packet); /// Checks and spawns the TSBPD thread if required. int checkLazySpawnLatencyThread(); From 652e35da0e7a9702496f7ef9fa95049b9e13c5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 7 Nov 2022 16:56:33 +0100 Subject: [PATCH 046/517] Applied changes for improved TSBPD and receiver buffer --- srtcore/buffer_rcv.cpp | 637 ++++++++++++++++++++++++++++++++------- srtcore/buffer_rcv.h | 301 +++++++++++++++--- srtcore/core.cpp | 264 +++++++++------- srtcore/core.h | 6 +- test/test_buffer_rcv.cpp | 5 +- 5 files changed, 952 insertions(+), 261 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7bfb00ad8..ede2033cb 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -98,7 +98,7 @@ namespace { * RcvBufferNew (circular buffer): * * |<------------------- m_iSize ----------------------------->| - * | |<----------- m_iMaxPosInc ------------>| | + * | |<----------- m_iMaxPosOff ------------>| | * | | | | * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] @@ -112,20 +112,22 @@ namespace { * thread safety: * m_iStartPos: CUDT::m_RecvLock * m_iLastAckPos: CUDT::m_AckLock - * m_iMaxPosInc: none? (modified on add and ack + * m_iMaxPosOff: none? (modified on add and ack */ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI) : m_entries(size) , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) - , m_iStartSeqNo(initSeqNo) + , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. , m_iStartPos(0) + , m_iEndPos(0) + , m_iDropPos(0) , m_iFirstNonreadPos(0) - , m_iMaxPosInc(0) + , m_iMaxPosOff(0) , m_iNotch(0) - , m_numOutOfOrderPackets(0) - , m_iFirstReadableOutOfOrder(-1) + , m_numRandomPackets(0) + , m_iFirstRandomMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -137,7 +139,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b CRcvBuffer::~CRcvBuffer() { - // Can be optimized by only iterating m_iMaxPosInc from m_iStartPos. + // Can be optimized by only iterating m_iMaxPosOff from m_iStartPos. for (FixedArray::iterator it = m_entries.begin(); it != m_entries.end(); ++it) { if (!it->pUnit) @@ -148,7 +150,13 @@ CRcvBuffer::~CRcvBuffer() } } -int CRcvBuffer::insert(CUnit* unit) +void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) +{ + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos + << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); +} + +CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); @@ -159,53 +167,264 @@ int CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); + int32_t avail_seq; + int avail_range; + if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); - return -2; + return InsertInfo(InsertInfo::BELATED); } + IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); if (offset >= (int)capacity()) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - return -3; + + // Calculation done for the sake of possible discrepancy + // in order to inform the caller what to do. + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_seq = packetAt(m_iStartPos).getSeqNo(); + avail_range = m_iEndPos - m_iStartPos; + } + else if (m_iDropPos == m_iEndPos) + { + avail_seq = SRT_SEQNO_NONE; + avail_range = 0; + } + else + { + avail_seq = packetAt(m_iDropPos).getSeqNo(); + + // We don't know how many packets follow it exactly, + // but in this case it doesn't matter. We know that + // at least one is there. + avail_range = 1; + } + + IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); + + return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); } // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - const int pos = (m_iStartPos + offset) % m_szSize; - if (offset >= m_iMaxPosInc) - m_iMaxPosInc = offset + 1; + const int newpktpos = incPos(m_iStartPos, offset); + const int prev_max_off = m_iMaxPosOff; + bool extended_end = false; + if (offset >= m_iMaxPosOff) + { + m_iMaxPosOff = offset + 1; + extended_end = true; + } // Packet already exists - SRT_ASSERT(pos >= 0 && pos < int(m_szSize)); - if (m_entries[pos].status != EntryState_Empty) + // (NOTE: the above extension of m_iMaxPosOff is + // possible even before checking that the packet + // exists because existence of a packet beyond + // the current max position is not possible). + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); - return -1; + IF_HEAVY_LOGGING(debugShowState((debug_source + " redundant").c_str())); + return InsertInfo(InsertInfo::REDUNDANT); } - SRT_ASSERT(m_entries[pos].pUnit == NULL); + SRT_ASSERT(m_entries[newpktpos].pUnit == NULL); m_pUnitQueue->makeUnitTaken(unit); - m_entries[pos].pUnit = unit; - m_entries[pos].status = EntryState_Avail; + m_entries[newpktpos].pUnit = unit; + m_entries[newpktpos].status = EntryState_Avail; countBytes(1, (int)unit->m_Packet.getLength()); + // Set to a value, if due to insertion there was added + // a packet that is earlier to be retrieved than the earliest + // currently available packet. + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numOutOfOrderPackets; - onInsertNotInOrderPacket(pos); + ++m_numRandomPackets; + onInsertNotInOrderPacket(newpktpos); } updateNonreadPos(); + + CPacket* avail_packet = NULL; + + if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) + { + avail_packet = &packetAt(m_iStartPos); + avail_range = m_iEndPos - m_iStartPos; + } + else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_range = 1; + } + else if (m_iDropPos != m_iEndPos) + { + avail_packet = &packetAt(m_iDropPos); + avail_range = 1; + } + IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); - return 0; + IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); + + if (avail_packet) + return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); + else + return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } +// This function should be called after having m_iEndPos +// has somehow be set to position of a non-empty cell. +// This can happen by two reasons: +// - the cell has been filled by incoming packet +// - the value has been reset due to shifted m_iStartPos +// This means that you have to search for a new gap and +// update the m_iEndPos and m_iDropPos fields, or set them +// both to the end of range. +// +// prev_max_pos should be the position represented by m_iMaxPosOff. +// Passed because it is already calculated in insert(), otherwise +// it would have to be calculated here again. +void CRcvBuffer::updateGapInfo(int prev_max_pos) +{ + int pos = m_iEndPos; + + // First, search for the next gap, max until m_iMaxPosOff. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status == EntryState_Empty) + { + break; + } + } + if (pos == prev_max_pos) + { + // Reached the end and found no gaps. + m_iEndPos = prev_max_pos; + m_iDropPos = prev_max_pos; + } + else + { + // Found a gap at pos + m_iEndPos = pos; + m_iDropPos = pos; // fallback, although SHOULD be impossible + // So, search for the first position to drop up to. + for ( ; pos != prev_max_pos; pos = incPos(pos)) + { + if (m_entries[pos].status != EntryState_Empty) + { + m_iDropPos = pos; + break; + } + } + } +} + +/// Request to remove from the receiver buffer +/// all packets with earlier sequence than @a seqno. +/// (Meaning, the packet with given sequence shall +/// be the first packet in the buffer after the operation). int CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -218,9 +437,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosInc -= len; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + m_iMaxPosOff -= len; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; const int iDropCnt = len; while (len > 0) @@ -235,13 +454,21 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Update positions m_iStartSeqNo = seqno; // Move forward if there are "read/drop" entries. + // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartPos; + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); + + IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; } @@ -250,7 +477,7 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosInc); + const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); return dropUpTo(end_seqno); } @@ -259,7 +486,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo); // TODO: count bytes as removed? - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); @@ -286,6 +513,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) // Check if units before m_iFirstNonreadPos are dropped. bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); + + // Start from here and search fort the next gap + m_iEndPos = m_iDropPos = m_iStartSeqNo; + updateGapInfo(end_pos); + if (needUpdateNonreadPos) { m_iFirstNonreadPos = m_iStartPos; @@ -293,10 +525,11 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } @@ -341,24 +574,47 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableOutOfOrder()) - m_iFirstReadableOutOfOrder = -1; - updateFirstReadableOutOfOrder(); + if (!checkFirstReadableRandom()) + m_iFirstRandomMsgPos = -1; + updateFirstReadableRandom(); } + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); return iDropCnt; } -int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) +bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const +{ + if (m_iStartPos == m_iEndPos) + { + // Initial contiguous region empty (including empty buffer). + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); + w_seq = m_iStartSeqNo; + return m_iMaxPosOff > 0; + } + + int end_off = offPos(m_iStartPos, m_iEndPos); + + w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + << " end=%" << w_seq); + + return (end_off < m_iMaxPosOff); +} + +int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstReadableOutOfOrder < 0) + if (!canReadInOrder && m_iFirstRandomMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstReadableOutOfOrder; + //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::readMessage. m_iStartSeqNo " << m_iStartSeqNo << " m_iStartPos " << m_iStartPos << " readPos " << readPos); @@ -367,7 +623,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) char* dst = data; int pkts_read = 0; int bytes_extracted = 0; // The total number of bytes extracted from the buffer. - const bool updateStartPos = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed + + int32_t out_seqlo = SRT_SEQNO_NONE; + int32_t out_seqhi = SRT_SEQNO_NONE; + for (int i = readPos;; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -381,6 +640,11 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); + if (out_seqlo == SRT_SEQNO_NONE) + out_seqlo = pktseqno; + + out_seqhi = pktseqno; + // unitsize can be zero const size_t unitsize = std::min(remain, pktsize); memcpy(dst, packet.m_pcData, unitsize); @@ -393,8 +657,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (m_tsbpd.isEnabled()) updateTsbPdTimeBase(packet.getMsgTimeStamp()); - if (m_numOutOfOrderPackets && !packet.getMsgOrderFlag()) - --m_numOutOfOrderPackets; + if (m_numRandomPackets && !packet.getMsgOrderFlag()) + --m_numRandomPackets; const bool pbLast = packet.getMsgBoundary() & PB_LAST; if (msgctrl && (packet.getMsgBoundary() & PB_FIRST)) @@ -409,11 +673,26 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) msgctrl->pktseq = pktseqno; releaseUnitInPos(i); - if (updateStartPos) + if (isReadingFromStart) { m_iStartPos = incPos(i); - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + + // m_iEndPos and m_iDropPos should be + // equal to m_iStartPos only if the buffer + // is empty - but in this case the extraction will + // not be done. Otherwise m_iEndPos should + // point to the first empty cell, and m_iDropPos + // point to the first busy cell after a gap, or + // at worst be equal to m_iEndPos. + + // Therefore none of them should be updated + // because they should be constantly updated + // on an incoming packet, while this function + // should not read further than to the first + // empty cell at worst. + + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(pktseqno); } else @@ -424,8 +703,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) if (pbLast) { - if (readPos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + if (readPos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; break; } } @@ -434,16 +713,59 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) releaseNextFillerEntries(); - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } + // Now that we have m_iStartPos potentially shifted, reinitialize + // m_iEndPos and m_iDropPos. + + int pend_pos = incPos(m_iStartPos, m_iMaxPosOff); + + // First check: is anything in the beginning + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + // If so, shift m_iEndPos up to the first nonexistent unit + // XXX Try to optimize search by splitting into two loops if necessary. + + m_iEndPos = incPos(m_iStartPos); + while (m_entries[m_iEndPos].status == EntryState_Avail) + { + m_iEndPos = incPos(m_iEndPos); + if (m_iEndPos == pend_pos) + break; + } + + // If we had first packet available, then there's also no drop pos. + m_iDropPos = m_iEndPos; + + } + else + { + // If not, reset m_iEndPos and search for the first after-drop candidate. + m_iEndPos = m_iStartPos; + m_iDropPos = m_iEndPos; + + while (m_entries[m_iDropPos].status != EntryState_Avail) + { + m_iDropPos = incPos(m_iDropPos); + if (m_iDropPos == pend_pos) + { + // Nothing found - set drop pos equal to end pos, + // which means there's no drop + m_iDropPos = m_iEndPos; + break; + } + } + } + + if (!m_tsbpd.isEnabled()) - // We need updateFirstReadableOutOfOrder() here even if we are reading inorder, + // We need updateFirstReadableRandom() here even if we are reading inorder, // incase readable inorder packets are all read out. - updateFirstReadableOutOfOrder(); + updateFirstReadableRandom(); const int bytes_read = int(dst - data); if (bytes_read < bytes_extracted) @@ -453,6 +775,10 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl) IF_RCVBUF_DEBUG(scoped_log.ss << " pldi64 " << *reinterpret_cast(data)); + if (pw_seqrange) + *pw_seqrange = make_pair(out_seqlo, out_seqhi); + + IF_HEAVY_LOGGING(debugShowState("readmsg")); return bytes_read; } @@ -530,8 +856,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iNotch = 0; m_iStartPos = p; - --m_iMaxPosInc; - SRT_ASSERT(m_iMaxPosInc >= 0); + --m_iMaxPosOff; + SRT_ASSERT(m_iMaxPosOff >= 0); m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); } else @@ -547,7 +873,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceeding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosInc, m_szSize, m_iFirstNonreadPos)) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -557,6 +883,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } + IF_HEAVY_LOGGING(debugShowState("readbuf")); return iBytesRead; } @@ -572,15 +899,12 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + return hasReadableInorderPkts() || (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const { - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - - return int(m_szSize + m_iFirstNonreadPos - m_iStartPos); + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -588,10 +912,10 @@ int CRcvBuffer::getTimespan_ms() const if (!m_tsbpd.isEnabled()) return 0; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return 0; - const int lastpos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); // Should not happen if TSBPD is enabled (reading out of order is not allowed). SRT_ASSERT(m_entries[lastpos].pUnit != NULL); if (m_entries[lastpos].pUnit == NULL) @@ -631,35 +955,28 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); - for (int i = m_iStartPos; i != end_pos; i = incPos(i)) + // Check the state of the very first packet first + if (m_entries[m_iStartPos].status == EntryState_Avail) { - // TODO: Maybe check status? - if (!m_entries[i].pUnit) - continue; - - const CPacket& packet = m_entries[i].pUnit->m_Packet; - const PacketInfo info = { packet.getSeqNo(), i != m_iStartPos, getPktTsbPdTime(packet.getMsgTimeStamp()) }; - return info; + SRT_ASSERT(m_entries[m_iStartPos].pUnit); + return (PacketInfo) { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + } + // If not, get the information from the drop + if (m_iDropPos != m_iEndPos) + { + const CPacket& pkt = packetAt(m_iDropPos); + return (PacketInfo) { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; } - const PacketInfo info = { -1, false, time_point() }; - return info; + return (PacketInfo) { SRT_SEQNO_NONE, false, time_point() }; } std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, (int) countReadable()); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); return std::pair(m_iStartSeqNo, seqno_last); } -size_t CRcvBuffer::countReadable() const -{ - if (m_iFirstNonreadPos >= m_iStartPos) - return m_iFirstNonreadPos - m_iStartPos; - return m_szSize + m_iFirstNonreadPos - m_iStartPos; -} - bool CRcvBuffer::isRcvDataReady(time_point time_now) const { const bool haveInorderPackets = hasReadableInorderPkts(); @@ -668,8 +985,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - return (m_numOutOfOrderPackets > 0 && m_iFirstReadableOutOfOrder != -1); + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); } if (!haveInorderPackets) @@ -693,11 +1010,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numOutOfOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstReadableOutOfOrder >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); + if (m_iFirstRandomMsgPos >= 0) { - SRT_ASSERT(m_numOutOfOrderPackets > 0); - const CPacket& packet = m_entries[m_iFirstReadableOutOfOrder].pUnit->m_Packet; + SRT_ASSERT(m_numRandomPackets > 0); + const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -742,9 +1059,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) { - --m_numOutOfOrderPackets; - if (pos == m_iFirstReadableOutOfOrder) - m_iFirstReadableOutOfOrder = -1; + --m_numRandomPackets; + if (pos == m_iFirstRandomMsgPos) + m_iFirstRandomMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -759,24 +1076,24 @@ void CRcvBuffer::releaseNextFillerEntries() releaseUnitInPos(pos); pos = incPos(pos); m_iStartPos = pos; - --m_iMaxPosInc; - if (m_iMaxPosInc < 0) - m_iMaxPosInc = 0; + --m_iMaxPosOff; + if (m_iMaxPosOff < 0) + m_iMaxPosOff = 0; } } // TODO: Is this function complete? There are some comments left inside. void CRcvBuffer::updateNonreadPos() { - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); // The empty position right after the last valid entry. + const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. int pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { - if (m_bMessageAPI && (m_entries[pos].pUnit->m_Packet.getMsgBoundary() & PB_FIRST) == 0) + if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; for (int i = pos; i != end_pos; i = incPos(i)) @@ -786,8 +1103,12 @@ void CRcvBuffer::updateNonreadPos() break; } + // m_iFirstNonreadPos is moved to the first position BEHIND + // the PB_LAST packet of the message. There's no guaratnee that + // the cell at this position isn't empty. + // Check PB_LAST only in message mode. - if (!m_bMessageAPI || m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { m_iFirstNonreadPos = incPos(i); break; @@ -818,7 +1139,7 @@ int CRcvBuffer::findLastMessagePkt() void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) { - if (m_numOutOfOrderPackets == 0) + if (m_numRandomPackets == 0) return; // If the following condition is true, there is already a packet, @@ -827,20 +1148,20 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstReadableOutOfOrder >= 0) + if (m_iFirstRandomMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. // So the should be unacknowledged packets. - SRT_ASSERT(m_iMaxPosInc > 0); + SRT_ASSERT(m_iMaxPosOff > 0); SRT_ASSERT(m_entries[insertPos].pUnit); - const CPacket& pkt = m_entries[insertPos].pUnit->m_Packet; + const CPacket& pkt = packetAt(insertPos); const PacketBoundary boundary = pkt.getMsgBoundary(); //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstReadableOutOfOrder = insertPos; + // m_iFirstRandomMsgPos = insertPos; // return; //} @@ -856,18 +1177,18 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) if (firstPktPos < 0) return; - m_iFirstReadableOutOfOrder = firstPktPos; + m_iFirstRandomMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableOutOfOrder() +bool CRcvBuffer::checkFirstReadableRandom() { - if (m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder < 0 || m_iMaxPosInc == 0) + if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosInc); + const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstReadableOutOfOrder; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -888,20 +1209,20 @@ bool CRcvBuffer::checkFirstReadableOutOfOrder() return false; } -void CRcvBuffer::updateFirstReadableOutOfOrder() +void CRcvBuffer::updateFirstReadableRandom() { - if (hasReadableInorderPkts() || m_numOutOfOrderPackets <= 0 || m_iFirstReadableOutOfOrder >= 0) + if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) return; - if (m_iMaxPosInc == 0) + if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numOutOfOrderPackets; + int outOfOrderPktsRemain = (int) m_numRandomPackets; // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; int posFirst = -1; int posLast = -1; @@ -940,7 +1261,7 @@ void CRcvBuffer::updateFirstReadableOutOfOrder() if (boundary & PB_LAST) { - m_iFirstReadableOutOfOrder = posFirst; + m_iFirstRandomMsgPos = posFirst; return; } @@ -955,7 +1276,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosInc - 1) % m_szSize; + const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; if (startPos == lastPos) return -1; @@ -997,7 +1318,7 @@ int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) return -1; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1055,19 +1376,19 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosInc << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; - if (m_tsbpd.isEnabled() && m_iMaxPosInc > 0) + if (m_tsbpd.isEnabled() && m_iMaxPosOff > 0) { const PacketInfo nextValidPkt = getFirstValidPacketInfo(); ss << "(TSBPD ready in "; if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosInc - 1); + const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1116,4 +1437,92 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) m_mavg.update(now, pkts, bytes, timespan_ms); } +int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) +{ + int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + + // Check if it's still inside the buffer + if (offset < 0 || offset >= m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + return SRT_SEQNO_NONE; + } + + // Start position + int pos = incPos(m_iStartPos, offset); + + // Ok; likely we should stand at the m_iEndPos position. + // If this given position is earlier than this, then + // m_iEnd stands on the first loss, unless it's equal + // to the position pointed by m_iMaxPosOff. + + int32_t ret_seq = SRT_SEQNO_NONE; + int ret_off = m_iMaxPosOff; + + int end_off = offPos(m_iStartPos, m_iEndPos); + if (pos < end_off) + { + // If m_iEndPos has such a value, then there are + // no loss packets at all. + if (end_off != m_iMaxPosOff) + { + ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_off = end_off; + } + } + else + { + // Could be strange, but just as the caller wishes: + // find the first loss since this point on + // You can't rely on m_iEndPos, you are beyond that now. + // So simply find the next hole. + + // REUSE offset as a control variable + for (; offset < m_iMaxPosOff; ++offset) + { + int pos = incPos(m_iStartPos, offset); + if (m_entries[pos].status == EntryState_Empty) + { + ret_off = offset; + ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + break; + } + } + } + + // If found no loss, just return this value and do not + // rewrite nor look for anything. + + // Also no need to search anything if only the beginning was + // being looked for. + if (ret_seq == SRT_SEQNO_NONE || !pw_end) + return ret_seq; + + // We want also the end range, so continue from where you + // stopped. + + // Start from ret_off + 1 because we know already that ret_off + // points to an empty cell. + for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + { + int pos = incPos(m_iStartPos, off); + if (m_entries[pos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + return ret_seq; + } + } + + // Fallback - this should be impossible, so issue a log. + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + + // Return this in the last resort - this could only be a situation when + // a packet has somehow disappeared, but it contains empty cells up to the + // end of buffer occupied range. This shouldn't be possible at all because + // there must be a valid packet at least at the last occupied cell. + return SRT_SEQNO_NONE; +} + + } // namespace srt diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 52e927f22..1cdce3ad8 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -20,28 +20,193 @@ namespace srt { -/* - * Circular receiver buffer. - * - * |<------------------- m_szSize ---------------------------->| - * | |<------------ m_iMaxPosInc ----------->| | - * | | | | - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] - * +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ - * | | - * | \__last pkt received - * | - * \___ m_iStartPos: first message to read - * - * m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) - * - * thread safety: - * start_pos_: CUDT::m_RecvLock - * first_unack_pos_: CUDT::m_AckLock - * max_pos_inc_: none? (modified on add and ack - * first_nonread_pos_: - */ +// +// Circular receiver buffer. +// +// |<------------------- m_szSize ---------------------------->| +// | |<------------ m_iMaxPosOff ----------->| | +// | | | | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] +// +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ +// | | | | +// | | | \__last pkt received +// | | | +// | | \___ m_iDropPos +// | | +// | \___ m_iEndPos +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// +// thread safety: +// start_pos_: CUDT::m_RecvLock +// first_unack_pos_: CUDT::m_AckLock +// max_pos_inc_: none? (modified on add and ack +// first_nonread_pos_: +// +// +// m_iStartPos: the first packet that should be read (might be empty) +// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos +// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// +// Operational rules: +// +// Initially: +// m_iStartPos = 0 +// m_iEndPos = 0 +// m_iDropPos = 0 +// +// When a packet has arrived, then depending on where it landed: +// +// 1. Position: next to the last read one and newest +// +// m_iStartPos unchanged. +// m_iEndPos shifted by 1 +// m_iDropPos = m_iEndPos +// +// 2. Position: after a loss, newest. +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if it was == m_iEndPos, set to this +// - otherwise unchanged +// +// 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos unchanged. +// m_iDropPos: +// - if m_iDropPos == m_iEndPos, set to this +// - if m_iDropPos %> this sequence, set to this +// - otherwise unchanged +// +// 4. Position: after a loss, sealing -- seq equal to position of m_iEndPos +// +// m_iStartPos unchanged. +// m_iEndPos: +// - since this position, search the first free cell +// - if reached the end of filled region (m_iMaxPosOff), stay there. +// m_iDropPos: +// - start from the value equal to m_iEndPos +// - walk at maximum to m_iMaxPosOff +// - find the first existing packet +// NOTE: +// If there are no "after gap" packets, then m_iMaxPosOff == m_iEndPos. +// If there is one existing packet, then one loss, then one packet, it +// should be that m_iEndPos = m_iStartPos %+ 1, m_iDropPos can reach +// to m_iStartPos %+ 2 position, and m_iMaxPosOff == m_iStartPos %+ 3. +// +// To wrap up: +// +// Let's say we have the following possibilities in a general scheme: +// +// +// [D] [C] [B] [A] (insertion cases) +// | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | +// +// WHEN INSERTING A NEW PACKET: +// +// If the incoming sequence maps to newpktpos that is: +// +// * newpktpos <% (start) : discard the packet and exit +// * newpktpos %> (size) : report discrepancy, discard and exit +// * newpktpos %> (start) and: +// * EXISTS: discard and exit (NOTE: could be also < (end)) +// [A]* seq == m_iMaxPosOff +// --> INC m_iMaxPosOff +// * m_iEndPos == previous m_iMaxPosOff +// * previous m_iMaxPosOff + 1 == m_iMaxPosOff +// --> m_iEndPos = m_iMaxPosOff +// --> m_iDropPos = m_iEndPos +// * otherwise (means the new packet caused a gap) +// --> m_iEndPos REMAINS UNCHANGED +// --> m_iDropPos = POSITION(m_iMaxPosOff) +// COMMENT: +// If this above condition isn't satisfied, then there are +// gaps, first at m_iEndPos, and m_iDropPos is at furthest +// equal to m_iMaxPosOff %- 1. The inserted packet is outside +// both the contiguous region and the following scratched region, +// so no updates on m_iEndPos and m_iDropPos are necessary. +// +// NOTE +// SINCE THIS PLACE seq cannot be a sequence of an existing packet, +// which means that earliest newpktpos == m_iEndPos, up to == m_iMaxPosOff -% 2. +// +// * otherwise (newpktpos <% max-pos): +// [D]* newpktpos == m_iEndPos: +// --> (search FIRST GAP and FIRST AFTER-GAP) +// --> m_iEndPos: increase until reaching m_iMaxPosOff +// * m_iEndPos <% m_iMaxPosOff: +// --> m_iDropPos = first VALID packet since m_iEndPos +% 1 +// * otherwise: +// --> m_iDropPos = m_iEndPos +// [B]* newpktpos %> m_iDropPos +// --> store, but do not update anything +// [C]* otherwise (newpktpos %> m_iEndPos && newpktpos <% m_iDropPos) +// --> store +// --> set m_iDropPos = newpktpos +// COMMENT: +// It is guaratneed that between m_iEndPos and m_iDropPos +// there is only a gap (series of empty cells). So wherever +// this packet lands, if it's next to m_iEndPos and before m_iDropPos +// it will be the only packet that violates the gap, hence this +// can be the only drop pos preceding the previous m_iDropPos. +// +// -- information returned to the caller should contain: +// 1. Whether adding to the buffer was successful. +// 2. Whether the "freshest" retrievable packet has been changed, that is: +// * in live mode, a newly added packet has earlier delivery time than one before +// * in stream mode, the newly added packet was at cell[0] +// * in message mode, if the newly added packet has: +// * completed the very first message +// * completed any message further than first that has out-of-order flag +// +// The information about a changed packet is important for the caller in +// live mode in order to notify the TSBPD thread. +// +// +// +// WHEN CHECKING A PACKET +// +// 1. Check the position at m_iStartPos. If there is a packet, +// return info at its position. +// +// 2. If position on m_iStartPos is empty, get the value of m_iDropPos. +// +// NOTE THAT: +// * if the buffer is empty, m_iDropPos == m_iStartPos and == m_iEndPos; +// note that m_iDropPos == m_iStartPos suffices to check that +// * if there is a packet in the buffer, but the first cell is empty, +// then m_iDropPos points to this packet, while m_iEndPos == m_iStartPos. +// Check then m_iStartPos == m_iEndPos to recognize it, and if then +// m_iDropPos isn't equal to them, you can read with dropping. +// * If cell[0] is valid, there could be only at worst cell[1] empty +// and cell[2] pointed by m_iDropPos. +// +// 3. In case of time-based checking for live mode, return empty packet info, +// if this packet's time is later than given time. +// +// WHEN EXTRACTING A PACKET +// +// 1. Extraction is only possible if there is a packet at cell[0]. +// 2. If there's no packet at cell[0], the application may request to +// drop up to the given packet, or drop the whole message up to +// the beginning of the next message. +// 3. In message mode, extraction can only extract a full message, so +// if there's no full message ready, nothing is extracted. +// 4. When the extraction region is defined, the m_iStartPos is shifted +// by the number of extracted packets. +// 5. If m_iEndPos <% m_iStartPos (after update), m_iEndPos should be +// set by searching from m_iStartPos up to m_iMaxPosOff for an empty cell. +// 6. m_iDropPos must be always updated. If m_iEndPos == m_iMaxPosOff, +// m_iDropPos is set to their value. Otherwise start from m_iEndPos +// and search a valid packet up to m_iMaxPosOff. +// 7. NOTE: m_iMaxPosOff is a delta, hence it must be set anew after update +// for m_iStartPos. +// class CRcvBuffer { @@ -54,6 +219,32 @@ class CRcvBuffer ~CRcvBuffer(); public: + + void debugShowState(const char* source); + + struct InsertInfo + { + enum Result { INSERTED = 0, REDUNDANT = -1, BELATED = -2, DISCREPANCY = -3 } result; + + // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. + + int first_seq; // sequence of the first available readable packet + time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. + int avail_range; + + InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, + time_point fp_time = time_point()) + : result(r), first_seq(fp_seq), first_time(fp_time), avail_range(range) + { + } + + InsertInfo() + : result(REDUNDANT), first_seq(SRT_SEQNO_NONE), avail_range(0) + { + } + + }; + /// Insert a unit into the buffer. /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) /// @@ -63,7 +254,8 @@ class CRcvBuffer /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. /// -3 if a packet is offset is ahead the buffer capacity. // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. - int insert(CUnit* unit); + InsertInfo insert(CUnit* unit); + void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -84,6 +276,17 @@ class CRcvBuffer /// @return the number of packets actually dropped. int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno); + /// Extract the "expected next" packet sequence. + /// Extract the past-the-end sequence for the first packet + /// that is expected to arrive next with preserving the packet order. + /// If the buffer is empty or the very first cell is lacking a packet, + /// it returns the sequence assigned to the first cell. Otherwise it + /// returns the sequence representing the first empty cell (the next + /// cell to the last received packet, if there are no loss-holes). + /// @param [out] w_seq: returns the sequence (always valid) + /// @return true if this sequence is followed by any valid packets + bool getContiguousEnd(int32_t& w_seq) const; + /// Read the whole message from one or several packets. /// /// @param [in,out] data buffer to write the message into. @@ -93,7 +296,7 @@ class CRcvBuffer /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. /// -1 on failure. - int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL); + int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL, std::pair* pw_seqrange = NULL); /// Read acknowledged data into a user buffer. /// @param [in, out] dst pointer to the target user buffer. @@ -179,11 +382,11 @@ class CRcvBuffer /// @note CSeqNo::seqoff(first, second) is 0 if nothing to read. std::pair getAvailablePacketsRange() const; - size_t countReadable() const; + int32_t getFirstLossSeq(int32_t fromseq, int32_t* opt_end = NULL); bool empty() const { - return (m_iMaxPosInc == 0); + return (m_iMaxPosOff == 0); } /// Return buffer capacity. @@ -195,6 +398,14 @@ class CRcvBuffer return m_szSize - 1; } + /// Returns the currently used number of cells, including + /// gaps with empty cells, or in other words, the distance + /// between the initial position and the youngest received packet. + size_t size() const + { + return m_iMaxPosOff; + } + int64_t getDrift() const { return m_tsbpd.drift(); } // TODO: make thread safe? @@ -225,6 +436,18 @@ class CRcvBuffer inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + inline int cmpPos(int pos2, int pos1) const + { + // XXX maybe not the best implementation, but this keeps up to the rule + int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + m_szSize - m_iStartPos; + int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + m_szSize - m_iStartPos; + + return off2 - off1; + } + + // NOTE: Assumes that pUnit != NULL + CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); @@ -247,9 +470,9 @@ class CRcvBuffer /// Scan for availability of out of order packets. void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstReadableOutOfOrder is still readable. - bool checkFirstReadableOutOfOrder(); - void updateFirstReadableOutOfOrder(); + // Check if m_iFirstRandomMsgPos is still readable. + bool checkFirstReadableRandom(); + void updateFirstReadableRandom(); int scanNotInOrderMessageRight(int startPos, int msgNo) const; int scanNotInOrderMessageLeft(int startPos, int msgNo) const; @@ -303,20 +526,26 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - FixedArray m_entries; + typedef FixedArray entries_t; + entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue int m_iStartSeqNo; int m_iStartPos; // the head position for I/O (inclusive) + int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosInc; // the furthest data position - int m_iNotch; // the starting read point of the first unit + int m_iMaxPosOff; // the furthest data position + int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) + + size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false - size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false - int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to - // read + /// Points to the first packet of a message that has out-of-order flag + /// and is complete (all packets from first to last are in the buffer). + /// If there is no such message in the buffer, it contains -1. + int m_iFirstRandomMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -342,6 +571,8 @@ class CRcvBuffer time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const; void updateTsbPdTimeBase(uint32_t usPktTimestamp); + bool isTsbPd() const { return m_tsbpd.isEnabled(); } + /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. std::string strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, const time_point& tsNow) const; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c614844dd..68824dd8e 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -292,7 +292,7 @@ void srt::CUDT::construct() m_bPeerTsbPd = false; m_iPeerTsbPdDelay_ms = 0; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bWakeOnRecv = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; @@ -5254,7 +5254,7 @@ void * srt::CUDT::tsbpd(void* param) CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond); CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker()); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5272,6 +5272,12 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; + HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + if (!self->m_bTLPktDrop) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5374,7 +5380,7 @@ void * srt::CUDT::tsbpd(void* param) * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. */ - self->m_bTsbPdAckWakeup = false; + self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); @@ -5396,7 +5402,7 @@ void * srt::CUDT::tsbpd(void* param) * - Closing the connection */ HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; + self->m_bWakeOnRecv = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); @@ -7782,10 +7788,28 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp m_tsLastSndTime.store(steady_clock::now()); } +bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) +{ + if (!m_pRcvBuffer) + { + LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); + return false; + } + { + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; + } + + return true; +} + int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { SRT_ASSERT(ctrlpkt.getMsgTimeStamp() != 0); - int32_t ack; // First unacknowledged packet seqnuence number (acknowledge up to ack). int nbsent = 0; int local_prevack = 0; @@ -7800,39 +7824,17 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) (void)l_saveback; // kill compiler warning: unused variable `l_saveback` [-Wunused-variable] local_prevack = m_iDebugPrevLastAck; - - string reason = "first lost"; // just for "a reason" of giving particular % for ACK #endif + string reason; // just for "a reason" of giving particular % for ACK -#if ENABLE_BONDING - dropToGroupRecvBase(); -#endif - - // The TSBPD thread may change the first lost sequence record (TLPKTDROP). - // To avoid it the m_RcvBufferLock has to be acquired. - UniqueLock bufflock(m_RcvBufferLock); - - { - // If there is no loss, the ACK is the current largest sequence number plus 1; - // Otherwise it is the smallest sequence number in the receiver loss list. - ScopedLock lock(m_RcvLossLock); - // TODO: Consider the Fresh Loss list as well!!! - ack = m_pRcvLossList->getFirstLostSeq(); - } - - // We don't need to check the length prematurely, - // if length is 0, this will return SRT_SEQNO_NONE. - // If so happened, simply use the latest received pkt + 1. - if (ack == SRT_SEQNO_NONE) - { - ack = CSeqNo::incseq(m_iRcvCurrSeqNo); - IF_HEAVY_LOGGING(reason = "expected next"); - } + int32_t ack; // First unacknowledged packet sequence number (acknowledge up to ack). + if (!getFirstNoncontSequence((ack), (reason))) + return nbsent; if (m_iRcvLastAckAck == ack) { HLOGC(xtlog.Debug, - log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); + log << CONID() << "sendCtrl(UMSG_ACK): last ACK %" << ack << "(" << reason << ") == last ACKACK"); return nbsent; } @@ -7840,7 +7842,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) // to save time on buffer processing and bandwidth/AS measurement, a lite ACK only feeds back an ACK number if (size == SEND_LITE_ACK) { - bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); @@ -7848,6 +7849,16 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) return nbsent; } + // Lock the group existence until this function ends. This will be useful + // also on other places. +#if ENABLE_BONDING + CUDTUnited::GroupKeeper gkeeper (uglobal(), m_parent); +#endif + + // There are new received packets to acknowledge, update related information. + /* tsbpd thread may also call ackData when skipping packet so protect code */ + UniqueLock bufflock(m_RcvBufferLock); + // IF ack %> m_iRcvLastAck // There are new received packets to acknowledge, update related information. if (CSeqNo::seqcmp(ack, m_iRcvLastAck) > 0) @@ -7894,21 +7905,32 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #endif IF_HEAVY_LOGGING(int32_t oldack = m_iRcvLastSkipAck); - // If TSBPD is enabled, then INSTEAD OF signaling m_RecvDataCond, - // signal m_RcvTsbPdCond. This will kick in the tsbpd thread, which - // will signal m_RecvDataCond when there's time to play for particular - // data packet. + // Signalling m_RecvDataCond is not dane when TSBPD is on. + // This signalling is done in file mode in order to keep the + // API reader thread sleeping until there is a "bigger portion" + // of data to read. In TSBPD mode this isn't done because every + // packet has its individual delivery time and its readiness is signed + // off by the TSBPD thread. HLOGC(xtlog.Debug, log << CONID() << "ACK: clip %" << oldack << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); if (m_bTsbPd) { - /* Newly acknowledged data, signal TsbPD thread */ + /* + There's no need to update TSBPD in the wake-on-recv state + from ACK because it is being done already in the receiver thread + when a newly inserted packet caused provision of a new candidate + that could be delivered soon. Also, this flag is only used in TSBPD + mode and can be only set to true in the TSBPD thread. + + // Newly acknowledged data, signal TsbPD thread // CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdAckWakeup) + // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread + if (m_bWakeOnRecv) tslcc.notify_one(); + + */ } else { @@ -7970,7 +7992,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID()<< "sendCtrl(UMSG_ACK): IPE: curr(" << reason << ") %" << ack + << " <% last %" << m_iRcvLastAck); return nbsent; } @@ -8763,8 +8786,28 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // is currently in the ACK-waiting state, it may never exit. if (m_bTsbPd) { - HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - rcvtscc.notify_one(); + // XXX Likely this is not necessary because: + // 1. In the recv-waiting state, that is, when TSBPD thread + // sleeps forever, it will be woken up anyway on packet + // reception. + // 2. If there are any packets in the buffer and the initial + // packet cell is empty (in which situation any drop could + // occur), TSBPD thread is sleeping timely, until the playtime + // of the first "drop up to" packet. Dropping changes nothing here. + // 3. If the buffer is empty, there's nothing "to drop up to", so + // this function will not change anything in the buffer and so + // in the reception state as well. + // 4. If the TSBPD thread is waiting until a play-ready packet is + // retrieved by the API call (in which case it is also sleeping + // forever until it's woken up by the API call), it may remain + // stalled forever, if the application isn't reading the play-ready + // packet. But this means that the application got stalled anyway. + // TSBPD when woken up could at best state that there's still a + // play-ready packet that is still not retrieved and fall back + // to sleep (forever). + + //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); + //rcvtscc.notify_one(); } } @@ -9701,14 +9744,14 @@ CUDT::time_point srt::CUDT::getPktTsbPdTime(void*, const CPacket& packet) SRT_ATR_UNUSED static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"}; -int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) +int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& w_new_inserted, steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs) { bool excessive SRT_ATR_UNUSED = true; // stays true unless it was successfully added // Loop over all incoming packets that were filtered out. // In case when there is no filter, there's just one packet in 'incoming', // the one that came in the input of this function. - for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt) + for (vector::const_iterator unitIt = incoming.begin(); unitIt != incoming.end() && !m_bBroken; ++unitIt) { CUnit * u = *unitIt; CPacket &rpkt = u->m_Packet; @@ -9787,7 +9830,26 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } } - buffer_add_result = m_pRcvBuffer->insert(u); + CRcvBuffer::InsertInfo info = m_pRcvBuffer->insert(u); + + // Remember this value in order to CHECK if there's a need + // to request triggering TSBPD in case when TSBPD is in the + // state of waiting forever and wants to know if there's any + // possible time to wake up known earlier than that. + + // Note that in case of the "builtin group reader" (its own + // buffer), there's no need to do it here because it has also + // its own TSBPD thread. + + if (info.result == CRcvBuffer::InsertInfo::INSERTED) + { + // This may happen multiple times in the loop, so update only if earlier. + if (w_next_tsbpd == time_point() || w_next_tsbpd > info.first_time) + w_next_tsbpd = info.first_time; + w_new_inserted = true; + } + buffer_add_result = int(info.result); + if (buffer_add_result < 0) { // addData returns -1 if at the m_iLastAckPos+offset position there already is a packet. @@ -9797,8 +9859,6 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - w_new_inserted = true; - IF_HEAVY_LOGGING(exc_type = "ACCEPTED"); excessive = false; if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC) @@ -10107,6 +10167,7 @@ int srt::CUDT::processData(CUnit* in_unit) int res = handleSocketPacketReception(incoming, (new_inserted), + (next_tsbpd_avail), (was_sent_in_order), (srt_loss_seqs)); @@ -10181,6 +10242,42 @@ int srt::CUDT::processData(CUnit* in_unit) return -1; } + // 1. This is set to true in case when TSBPD during the last check + // has seen no packet candidate to ever deliver, hence it needs + // an update on that. Note that this is also false if TSBPD thread + // isn't running. + // 2. If next_tsbpd_avail is set, it means that in the buffer there is + // a new packet that precedes the previously earliest available packet. + // This means that if TSBPD was sleeping up to the time of this earliest + // delivery (after drop), this time we have received a packet to be delivered + // earlier than that, so we need to notify TSBPD immediately so that it + // updates this itself, not sleep until the previously set time. + + // The meaning of m_bWakeOnRecv: + // - m_bWakeOnRecv is set by TSBPD thread and means that it wishes to be woken up + // on every received packet. Hence we signal always if a new packet was inserted. + // - even if TSBPD doesn't wish to be woken up on every reception (because it sleeps + // until the play time of the next deliverable packet), it will be woken up when + // next_tsbpd_avail is set because it means this time is earlier than the time until + // which TSBPD sleeps, so it must be woken up prematurely. It might be more performant + // to simply update the sleeping end time of TSBPD, but there's no way to do it, so + // we simply wake TSBPD up and count on that it will update its sleeping settings. + + // XXX Consider: as CUniqueSync locks m_RecvLock, it means that the next instruction + // gets run only when TSBPD falls asleep again. Might be a good idea to record the + // TSBPD end sleeping time - as an alternative to m_bWakeOnRecv - and after locking + // a mutex check this time again and compare it against next_tsbpd_avail; might be + // that if this difference is smaller than "dirac" (could be hard to reliably compare + // this time, unless it's set from this very value), there's no need to wake the TSBPD + // thread because it will wake up on time requirement at the right time anyway. + if (m_bTsbPd && ((m_bWakeOnRecv && new_inserted) || next_tsbpd_avail != time_point())) + { + HLOGC(qrlog.Debug, log << "processData: will SIGNAL TSBPD for socket. WakeOnRecv=" << m_bWakeOnRecv + << " new_inserted=" << new_inserted << " next_tsbpd_avail=" << FormatTime(next_tsbpd_avail)); + CUniqueSync tsbpd_cc(m_RecvLock, m_RcvTsbPdCond); + tsbpd_cc.notify_all(); + } + if (incoming.empty()) { // Treat as excessive. This is when a filter cumulates packets @@ -10197,6 +10294,10 @@ int srt::CUDT::processData(CUnit* in_unit) sendLossReport(srt_loss_seqs); } + // This should not be required with the new receiver buffer because + // signalling happens on every packet reception, if it has changed the + // earliest packet position. + /* if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10206,6 +10307,7 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); } + */ } // Separately report loss records of those reported by a filter. @@ -10218,6 +10320,8 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); + // XXX unsure as to whether this should change anything in the TSBPD conditions. + // Might be that this trigger is not necessary. if (m_bTsbPd) { HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); @@ -10357,67 +10461,6 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) setInitialRcvSeq(source->m_iRcvLastSkipAck); } -// XXX This function is currently unused. It should be fixed and put into use. -// See the blocked call in CUDT::processData(). -// XXX REVIEW LOCKS WHEN REACTIVATING! -srt::CUDT::loss_seqs_t srt::CUDT::defaultPacketArrival(void* vself, CPacket& pkt) -{ -// [[using affinity(m_pRcvBuffer->workerThread())]]; - CUDT* self = (CUDT*)vself; - loss_seqs_t output; - - // XXX When an alternative packet arrival callback is installed - // in case of groups, move this part to the groupwise version. - - if (self->m_parent->m_GroupOf) - { - groups::SocketData* gi = self->m_parent->m_GroupMemberData; - if (gi->rcvstate < SRT_GST_RUNNING) // PENDING or IDLE, tho PENDING is unlikely - { - HLOGC(qrlog.Debug, log << "defaultPacketArrival: IN-GROUP rcv state transition to RUNNING. NOT checking for loss"); - gi->rcvstate = SRT_GST_RUNNING; - return output; - } - } - - const int initial_loss_ttl = (self->m_bPeerRexmitFlag) ? self->m_iReorderTolerance : 0; - - int seqdiff = CSeqNo::seqcmp(pkt.m_iSeqNo, self->m_iRcvCurrSeqNo); - - HLOGC(qrlog.Debug, log << "defaultPacketArrival: checking sequence " << pkt.m_iSeqNo - << " against latest " << self->m_iRcvCurrSeqNo << " (distance: " << seqdiff << ")"); - - // Loss detection. - if (seqdiff > 1) // packet is later than the very subsequent packet - { - const int32_t seqlo = CSeqNo::incseq(self->m_iRcvCurrSeqNo); - const int32_t seqhi = CSeqNo::decseq(pkt.m_iSeqNo); - - { - // If loss found, insert them to the receiver loss list - ScopedLock lg (self->m_RcvLossLock); - self->m_pRcvLossList->insert(seqlo, seqhi); - - if (initial_loss_ttl) - { - // pack loss list for (possibly belated) NAK - // The LOSSREPORT will be sent in a while. - self->m_FreshLoss.push_back(CRcvFreshLoss(seqlo, seqhi, initial_loss_ttl)); - HLOGF(qrlog.Debug, "defaultPacketArrival: added loss sequence %d-%d (%d) with tolerance %d", seqlo, seqhi, - 1+CSeqNo::seqcmp(seqhi, seqlo), initial_loss_ttl); - } - } - - if (!initial_loss_ttl) - { - // old code; run immediately when tolerance = 0 - // or this feature isn't used because of the peer - output.push_back(make_pair(seqlo, seqhi)); - } - } - - return output; -} #endif /// This function is called when a packet has arrived, which was behind the current @@ -11089,7 +11132,10 @@ int srt::CUDT::checkNAKTimer(const steady_clock::time_point& currtime) * not knowing what to retransmit when the only NAK sent by receiver is lost, * all packets past last ACK are retransmitted (rexmitMethod() == SRM_FASTREXMIT). */ + enterCS(m_RcvLossLock); const int loss_len = m_pRcvLossList->getLossLength(); + leaveCS(m_RcvLossLock); + SRT_ASSERT(loss_len >= 0); int debug_decision = BECAUSE_NO_REASON; diff --git a/srtcore/core.h b/srtcore/core.h index 81979bb83..5bb66d436 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -661,6 +661,7 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM); void handshakeDone() @@ -925,7 +926,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1061,13 +1062,14 @@ class CUDT /// /// @param incoming [in] The packet coming from the network medium /// @param w_new_inserted [out] Set false, if the packet already exists, otherwise true (packet added) + /// @param w_next_tsbpd [out] Get the TSBPD time of the earliest playable packet after insertion /// @param w_was_sent_in_order [out] Set false, if the packet was belated, but had no R flag set. /// @param w_srt_loss_seqs [out] Gets inserted a loss, if this function has detected it. /// /// @return 0 The call was successful (regardless if the packet was accepted or not). /// @return -1 The call has failed: no space left in the buffer. /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). - int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); + int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, sync::steady_clock::time_point& w_next_tsbpd, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); // This function is to return the packet's play time (time when // it is submitted to the reading application) of the given packet. diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 4b0d9c833..7d70d6d92 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -72,7 +72,10 @@ class CRcvBufferReadMsg EXPECT_TRUE(packet.getMsgOrderFlag()); } - return m_rcv_buffer->insert(unit); + auto info = m_rcv_buffer->insert(unit); + // XXX extra checks? + + return int(info.result); } /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed From 36c1f674f39a1c4b116c0496ed873936a8a78ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 8 Nov 2022 17:24:59 +0100 Subject: [PATCH 047/517] Updated usage of shortcuts and new names --- srtcore/buffer_rcv.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ede2033cb..3c0ce3e5c 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,15 +75,15 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosInc) % iSize]. + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosInc, size_t iSize, int iFirstNonreadPos) + bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosInc) % iSize; + const int iLastPos = (iStartPos + iMaxPosOff) % iSize; const bool isOverrun = iLastPos < iStartPos; if (isOverrun) @@ -499,7 +499,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) continue; // TODO: Break the loop if a massege has been found. No need to search further. - const int32_t msgseq = m_entries[i].pUnit->m_Packet.getMsgSeq(m_bPeerRexmitFlag); + const int32_t msgseq = packetAt(i).getMsgSeq(m_bPeerRexmitFlag); if (msgseq == msgno) { ++iDropCnt; @@ -551,7 +551,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) { // Don't drop messages, if all its packets are already in the buffer. // TODO: Don't drop a several-packet message if all packets are in the buffer. - if (m_entries[i].pUnit && m_entries[i].pUnit->m_Packet.getMsgBoundary() == PB_SOLO) + if (m_entries[i].pUnit && packetAt(i).getMsgBoundary() == PB_SOLO) continue; dropUnitInPos(i); @@ -636,7 +636,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairm_Packet; + const CPacket& packet = packetAt(i); const size_t pktsize = packet.getLength(); const int32_t pktseqno = packet.getSeqNo(); @@ -828,7 +828,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) return -1; } - const srt::CPacket& pkt = m_entries[p].pUnit->m_Packet; + const srt::CPacket& pkt = packetAt(p); if (bTsbPdEnabled) { @@ -935,8 +935,8 @@ int CRcvBuffer::getTimespan_ms() const return 0; const steady_clock::time_point startstamp = - getPktTsbPdTime(m_entries[startpos].pUnit->m_Packet.getMsgTimeStamp()); - const steady_clock::time_point endstamp = getPktTsbPdTime(m_entries[lastpos].pUnit->m_Packet.getMsgTimeStamp()); + getPktTsbPdTime(packetAt(startpos).getMsgTimeStamp()); + const steady_clock::time_point endstamp = getPktTsbPdTime(packetAt(lastpos).getMsgTimeStamp()); if (endstamp < startstamp) return 0; @@ -1006,7 +1006,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no { if (hasInorderPackets) { - const CPacket& packet = m_entries[m_iStartPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iStartPos); const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } @@ -1014,7 +1014,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no if (m_iFirstRandomMsgPos >= 0) { SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = m_entries[m_iFirstRandomMsgPos].pUnit->m_Packet; + const CPacket& packet = packetAt(m_iFirstRandomMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1055,9 +1055,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) return false; if (m_tsbpd.isEnabled()) { - updateTsbPdTimeBase(m_entries[pos].pUnit->m_Packet.getMsgTimeStamp()); + updateTsbPdTimeBase(packetAt(pos).getMsgTimeStamp()); } - else if (m_bMessageAPI && !m_entries[pos].pUnit->m_Packet.getMsgOrderFlag()) + else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { --m_numRandomPackets; if (pos == m_iFirstRandomMsgPos) @@ -1128,7 +1128,7 @@ int CRcvBuffer::findLastMessagePkt() { SRT_ASSERT(m_entries[i].pUnit); - if (m_entries[i].pUnit->m_Packet.getMsgBoundary() & PB_LAST) + if (packetAt(i).getMsgBoundary() & PB_LAST) { return i; } @@ -1193,7 +1193,7 @@ bool CRcvBuffer::checkFirstReadableRandom() if (!m_entries[pos].pUnit) return false; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) return false; @@ -1236,7 +1236,7 @@ void CRcvBuffer::updateFirstReadableRandom() continue; } - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgOrderFlag()) // Skip in order packet { @@ -1287,7 +1287,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const if (!m_entries[pos].pUnit) break; - const CPacket& pkt = m_entries[pos].pUnit->m_Packet; + const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { @@ -1376,7 +1376,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (enable_debug_log) { ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosInc=" << m_iMaxPosOff << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; } ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1392,7 +1392,7 @@ string CRcvBuffer::strFullnessState(bool enable_debug_log, int iFirstUnackSeqNo, if (m_entries[iLastPos].pUnit) { ss << ", timespan "; - const uint32_t usPktTimestamp = m_entries[iLastPos].pUnit->m_Packet.getMsgTimeStamp(); + const uint32_t usPktTimestamp = packetAt(iLastPos).getMsgTimeStamp(); ss << count_milliseconds(m_tsbpd.getPktTsbPdTime(usPktTimestamp) - nextValidPkt.tsbpd_time); ss << " ms"; } From 8335cbec1a7477a387be35f470e068779ccef41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 10 Nov 2022 14:58:42 +0100 Subject: [PATCH 048/517] Fixed some logs formatting --- srtcore/core.cpp | 34 +++++++++++++++++++++++----------- srtcore/packet.cpp | 22 ++++++++++++++++++---- srtcore/queue.cpp | 4 ++-- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b480acbd3..950cb0b72 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5274,11 +5274,20 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; - HLOGC(tslog.Debug, log << self->CONID() << "grp/tsbpd: packet check: %" - << info.seqno << " T=" << FormatTime(tsNextDelivery) - << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) - << " ready=" << is_time_to_deliver - << " ondrop=" << info.seq_gap); +#if ENABLE_HEAVY_LOGGING + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: NO PACKETS"); + } + else + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + } +#endif if (!self->m_bTLPktDrop) { @@ -5316,7 +5325,7 @@ void * srt::CUDT::tsbpd(void* param) { HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + << FormatDuration(steady_clock::now() - info.tsbpd_time) << ")"); /* * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available @@ -5375,6 +5384,8 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } + SRT_ATR_UNUSED bool wakeup_on_signal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5385,9 +5396,9 @@ void * srt::CUDT::tsbpd(void* param) self->m_bWakeOnRecv = false; HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5410,7 +5421,8 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); @@ -7488,8 +7500,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) m_dCongestionWindow = m_CongCtl->cgWindowSize(); #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, - log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us (" - << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << std::setprecision(3) << m_dCongestionWindow); #endif } diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index cbe4dd90d..e7ddb9db1 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -263,7 +263,7 @@ void CPacket::setLength(size_t len, size_t cap) #if ENABLE_HEAVY_LOGGING // Debug only -static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size) +static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, void* rparam, const size_t size) { // This may be changed over time, so use special interpretation // only for certain types, and still display all data, no matter @@ -288,9 +288,15 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } bool interp_as_seq = (pkttype == UMSG_LOSSREPORT || pkttype == UMSG_DROPREQ); + bool display_dec = (pkttype == UMSG_ACK || pkttype == UMSG_ACKACK || pkttype == UMSG_DROPREQ); out << " [ "; - for (size_t i = 0; i < size; ++i) + + // Will be effective only for hex/oct. + out << std::showbase; + + const size_t size32 = size/4; + for (size_t i = 0; i < size32; ++i) { int32_t val = ((int32_t*)rparam)[i]; if (interp_as_seq) @@ -302,8 +308,16 @@ static std::string FormatNumbers(UDTMessageType pkttype, const int32_t* lparam, } else { - out << std::showpos << std::hex << val << "/" << std::dec << val; + if (!display_dec) + { + out << std::hex; + out << val << "/"; + out << std::dec; + } + out << val; + } + out << " "; } out << "]"; @@ -315,7 +329,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, { // Set (bit-0 = 1) and (bit-1~15 = type) setControl(pkttype); - HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << FormatNumbers(pkttype, lparam, rparam, size)); + HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype) << " " << FormatNumbers(pkttype, lparam, rparam, size)); // Set additional information and control information field switch (pkttype) diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 8d6727268..1627a0c6b 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1042,8 +1042,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << " " << FormatDuration(tsNow - tsLastReq) + << " passed since last connection request."); continue; } From e27c268dbc8a83e55c58a69bc2f53994ada18de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 28 Nov 2022 12:58:24 +0100 Subject: [PATCH 049/517] Fixed compile errors after merge --- apps/apputil.cpp | 3 ++- testing/testmedia.cpp | 12 +++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 0cfa6875a..dc96c2245 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "srt.h" // Required for SRT_SYNC_CLOCK_* definitions. #include "apputil.hpp" #include "netinet_any.h" @@ -503,4 +504,4 @@ bool IsTargetAddrSelf(const sockaddr* , const sockaddr* ) // prevention from connecting to self will not be in force. return false; } -#endif \ No newline at end of file +#endif diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 895173531..115537573 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1283,15 +1283,13 @@ void SrtCommon::ConnectClient(string host, int port) { // Check if trying to connect to self. sockaddr_any lsa; - int size = lsa.size(); - srt_getsockname(m_sock, &lsa, &size); + srt_getsockname(m_sock, lsa.get(), &lsa.len); - if (lsa.hport() == port && IsTargetAddrSelf(&lsa, psa)) + if (lsa.hport() == port && IsTargetAddrSelf(lsa.get(), sa.get())) { - Verb() << "ERROR: Trying to connect to SELF address " << SockaddrToString(psa) - << " with socket bound to " << SockaddrToString(&lsa); - UDT::ERRORINFO inval(MJ_SETUP, MN_INVAL, 0); - Error(inval, "srt_connect"); + Verb() << "ERROR: Trying to connect to SELF address " << sa.str() + << " with socket bound to " << lsa.str(); + Error("srt_connect", 0, SRT_EINVPARAM); } } Verb() << "Connecting to " << host << ":" << port << " ... " << VerbNoEOL; From 2424c99c427b5036413bfd1e50092aec7222f634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Dec 2022 17:39:04 +0100 Subject: [PATCH 050/517] Fixing implicit integer and wrong use of SRT_ERROR and SRT_INVALID_SOCKET --- CMakeLists.txt | 4 + apps/socketoptions.hpp | 2 +- common/devel_util.h | 74 +++++++ examples/recvlive.cpp | 10 - examples/recvmsg.cpp | 215 ++++++++++++++++++ examples/sendmsg.cpp | 210 ++++++++++++++++++ examples/test-c-client.c | 2 +- examples/test-c-server.c | 2 +- srtcore/api.cpp | 408 ++++++++++++++++++----------------- srtcore/api.h | 60 +++--- srtcore/buffer_rcv.cpp | 4 + srtcore/buffer_snd.cpp | 52 +++-- srtcore/buffer_snd.h | 25 ++- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 307 ++++++++++++++------------ srtcore/core.h | 61 +++--- srtcore/crypto.cpp | 2 +- srtcore/epoll.cpp | 24 +-- srtcore/epoll.h | 23 +- srtcore/fec.cpp | 2 +- srtcore/group.cpp | 6 +- srtcore/group.h | 2 +- srtcore/handshake.cpp | 4 +- srtcore/handshake.h | 2 +- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 62 +++--- srtcore/queue.h | 32 +-- srtcore/srt.h | 105 ++++----- srtcore/srt_c_api.cpp | 90 ++++---- srtcore/tsbpd_time.cpp | 2 +- srtcore/udt.h | 2 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +- test/test_crypto.cpp | 6 +- test/test_epoll.cpp | 24 +-- test/test_fec_rebuilding.cpp | 6 +- testing/testmedia.cpp | 102 ++++----- testing/testmedia.hpp | 10 +- 41 files changed, 1280 insertions(+), 713 deletions(-) create mode 100644 common/devel_util.h create mode 100644 examples/recvmsg.cpp create mode 100644 examples/sendmsg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4de83e89e..01c4eef17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1325,6 +1325,10 @@ if (ENABLE_EXAMPLES) srt_add_example(recvfile.cpp) + srt_add_example(sendmsg.cpp) + + srt_add_example(recvmsg.cpp) + srt_add_example(test-c-client.c) srt_add_example(example-client-nonblock.c) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 7ca0451a2..9b9f7e73b 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -67,7 +67,7 @@ struct SocketOption template<> inline int SocketOption::setso(int socket, int /*ignored*/, int sym, const void* data, size_t size) { - return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_setsockopt(SRTSOCKET(socket), 0, SRT_SOCKOPT(sym), data, (int) size); } #if ENABLE_BONDING diff --git a/common/devel_util.h b/common/devel_util.h new file mode 100644 index 000000000..1944e2613 --- /dev/null +++ b/common/devel_util.h @@ -0,0 +1,74 @@ + +template +struct IntWrapper +{ + INT v; + + IntWrapper() {} + explicit IntWrapper(INT val): v(val) {} + + bool operator==(const IntWrapper& x) const + { + return v == x.v; + } + + bool operator!=(const IntWrapper& x) const + { + return !(*this == x); + } + + explicit operator INT() const + { + return v; + } + + template + friend T& operator<<(T& out, const IntWrapper& x) + { + out << x.v; + return out; + } + + bool operator<(const IntWrapper& w) const + { + return v < w.v; + } +}; + +template +struct IntWrapperLoose: IntWrapper +{ + typedef IntWrapper base_t; + explicit IntWrapperLoose(INT val): base_t(val) {} + + bool operator==(const IntWrapper& x) const + { + return this->v == x.v; + } + + friend bool operator==(const IntWrapper& x, const IntWrapperLoose& y) + { + return x.v == y.v; + } + + bool operator==(INT val) const + { + return this->v == val; + } + + friend bool operator==(INT val, const IntWrapperLoose& x) + { + return val == x.v; + } + + operator INT() const + { + return this->v; + } +}; + + +//typedef IntWrapper SRTSOCKET; +//typedef IntWrapper SRTSTATUS; +//typedef IntWrapperLoose SRTSTATUS_LOOSE; + diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index b2a026d03..81c2b1ef7 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -53,16 +53,6 @@ int main(int argc, char* argv[]) return 0; } - // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, - // although there's also lots of other options to be set, for which there's a convenience option, - // SRTO_TRANSTYPE. - // SRT_TRANSTYPE tt = SRTT_LIVE; - // if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_TRANSTYPE, &tt, sizeof tt)) - // { - // cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; - // return 0; - // } - bool no = false; if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_RCVSYN, &no, sizeof no)) { diff --git a/examples/recvmsg.cpp b/examples/recvmsg.cpp new file mode 100644 index 000000000..008eb3928 --- /dev/null +++ b/examples/recvmsg.cpp @@ -0,0 +1,215 @@ +#ifndef WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +string CreateFilename(string fmt, int ord) +{ + ostringstream os; + + size_t pos = fmt.find('%'); + if (pos == string::npos) + os << fmt << ord << ".out"; + else + { + os << fmt.substr(0, pos) << ord << fmt.substr(pos+1); + } + return os.str(); +} + +int main(int argc, char* argv[]) +{ + string service("9000"); + if (argc > 1) + service = argv[1]; + + if (service == "--help") + { + cout << "usage: recvmsg [server_port] [filepattern]" << endl; + return 0; + } + + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) + { + cout << "illegal port number or port is busy.\n" << endl; + return 1; + } + + string outfileform; + if (argc > 2) + { + outfileform = argv[2]; + } + + // use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + SRTSOCKET sfd = srt_create_socket(); + if (SRT_INVALID_SOCK == sfd) + { + cout << "srt_socket: " << srt_getlasterror_str() << endl; + return 1; + } + + int file_mode = SRTT_FILE; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_TRANSTYPE, &file_mode, sizeof file_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + bool message_mode = true; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + if (SRT_ERROR == srt_bind(sfd, res->ai_addr, res->ai_addrlen)) + { + cout << "srt_bind: " << srt_getlasterror_str() << endl; + return 0; + } + + freeaddrinfo(res); + + cout << "server is ready at port: " << service << endl; + + if (SRT_ERROR == srt_listen(sfd, 10)) + { + cout << "srt_listen: " << srt_getlasterror_str() << endl; + return 1; + } + + char data[4096]; + + srt::sockaddr_any remote; + + int afd = srt_accept(sfd, remote.get(), &remote.len); + + if (afd == SRT_INVALID_SOCK) + { + cout << "srt_accept: " << srt_getlasterror_str() << endl; + return 1; + } + + cout << "Connection from " << remote.str() << " established\n"; + + bool save_to_files = true; + + if (outfileform != "") + save_to_files = true; + + int ordinal = 1; + + // the event loop + while (true) + { + SRT_SOCKSTATUS status = srt_getsockstate(afd); + if ((status == SRTS_BROKEN) || + (status == SRTS_NONEXIST) || + (status == SRTS_CLOSED)) + { + cout << "source disconnected. status=" << status << endl; + srt_close(afd); + break; + } + + int ret = srt_recvmsg(afd, data, sizeof(data)); + if (ret == SRT_ERROR) + { + cout << "srt_recvmsg: " << srt_getlasterror_str() << endl; + break; + } + if (ret == 0) + { + cout << "EOT\n"; + break; + } + + if (ret < 5) + { + cout << "WRONG MESSAGE SYNTAX\n"; + break; + } + + if (save_to_files) + { + string fname = CreateFilename(outfileform, ordinal++); + ofstream ofile(fname); + + if (!ofile.good()) + { + cout << "ERROR: can't create file: " << fname << " - skipping message\n"; + continue; + } + + ofile.write(data, ret); + ofile.close(); + cout << "Written " << ret << " bytes of message to " << fname << endl; + } + else + { + union + { + char chars[4]; + int32_t intval; + } first4; + + copy(data, data + 4, first4.chars); + + cout << "[" << ret << "B " << ntohl(first4.intval) << "] "; + for (int i = 4; i < ret; ++i) + cout << ShowChar(data[i]); + cout << endl; + } + } + + srt_close(afd); + srt_close(sfd); + + // use this function to release the UDT library + srt_cleanup(); + + return 0; +} + +// Local Variables: +// c-file-style: "ellemtel" +// c-basic-offset: 3 +// compile-command: "g++ -Wall -O2 -std=c++11 -I.. -I../srtcore -o recvlive recvlive.cpp -L.. -lsrt -lpthread -L/usr/local/opt/openssl/lib -lssl -lcrypto" +// End: diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp new file mode 100644 index 000000000..911de48c9 --- /dev/null +++ b/examples/sendmsg.cpp @@ -0,0 +1,210 @@ +#ifndef _WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +int main(int argc, char* argv[]) +{ + if ((argc != 4) || (0 == atoi(argv[2]))) + { + cout << "usage: sendmsg server_ip server_port source_filename" << endl; + return -1; + } + + // Use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + struct addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + SRTSOCKET fhandle = srt_create_socket(); + // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, + // although there's also lots of other options to be set, for which there's a convenience option, + // SRTO_TRANSTYPE. + SRT_TRANSTYPE tt = SRTT_FILE; + srt_setsockopt(fhandle, 0, SRTO_TRANSTYPE, &tt, sizeof tt); + + bool message_mode = true; + srt_setsockopt(fhandle, 0, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode); + + if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) + { + cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; + return -1; + } + + // Connect to the server, implicit bind. + if (SRT_ERROR == srt_connect(fhandle, peer->ai_addr, peer->ai_addrlen)) + { + int rej = srt_getrejectreason(fhandle); + cout << "connect: " << srt_getlasterror_str() << ":" << srt_rejectreason_str(rej) << endl; + return -1; + } + + freeaddrinfo(peer); + + string source_fname = argv[3]; + + bool use_filelist = false; + if (source_fname[0] == '+') + { + use_filelist = true; + source_fname = source_fname.substr(1); + } + + istream* pin; + ifstream fin; + if (source_fname == "-") + { + pin = &cin; + } + else + { + fin.open(source_fname.c_str()); + pin = &fin; + } + + // The syntax is: + // + // 1. If the first character is +, it's followed by TTL in milliseconds. + // 2. Otherwise the first number is the ID, followed by a space, to be filled in first 4 bytes. + // 3. Rest of the characters, up to the end of line, should be put into a solid block and sent at once. + + int status = SRT_STATUS_OK; + + int ordinal = 1; + int lpos = 0; + + for (;;) + { + string line; + int32_t id = 0; + int ttl = -1; + + getline(*pin, (line)); + if (pin->eof()) + break; + + // Interpret + if (line.size() < 2) + continue; + + if (use_filelist) + { + // The line should contain [+TTL] FILENAME + char fname[1024] = ""; + if (line[0] == '+') + { + int nparsed = sscanf(line.c_str(), "+%d %n%1000s", &ttl, &lpos, fname); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + line = fname; + } + + ifstream ifile (line); + + id = ordinal; + ++ordinal; + + if (!ifile.good()) + { + cout << "ERROR: file '" << line << "' cannot be read, skipping\n"; + continue; + } + + line = string(istreambuf_iterator(ifile), istreambuf_iterator()); + } + else + { + int lpos = 0; + + int nparsed = 0; + if (line[0] == '+') + { + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + else + { + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + if (nparsed != 1) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + } + + union + { + char chars[4]; + int32_t intval; + } first4; + first4.intval = htonl(id); + + vector input; + copy(first4.chars, first4.chars+4, back_inserter(input)); + copy(line.begin() + lpos, line.end(), back_inserter(input)); + + // CHECK CODE + // cout << "WILL SEND: TTL=" << ttl << " "; + // transform(input.begin(), input.end(), + // ostream_iterator(cout), ShowChar); + // cout << endl; + + int nsnd = srt_sendmsg(fhandle, &input[0], input.size(), ttl, false); + if (nsnd == SRT_ERROR) + { + cout << "SRT ERROR: " << srt_getlasterror_str() << endl; + status = SRT_ERROR; + break; + } + } + + srt_close(fhandle); + + // Signal to the SRT library to clean up all allocated sockets and resources. + srt_cleanup(); + + return status; +} diff --git a/examples/test-c-client.c b/examples/test-c-client.c index a4d99745c..71f6d30e4 100644 --- a/examples/test-c-client.c +++ b/examples/test-c-client.c @@ -59,7 +59,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/examples/test-c-server.c b/examples/test-c-server.c index f81f297eb..f7faf6fba 100644 --- a/examples/test-c-server.c +++ b/examples/test-c-server.c @@ -54,7 +54,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 1cd67cea5..78ba3a346 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -229,7 +229,7 @@ srt::CUDTUnited::~CUDTUnited() string srt::CUDTUnited::CONID(SRTSOCKET sock) { - if (sock == 0) + if (int32_t(sock) <= 0) // both SRT_INVALID_SOCK and SRT_SOCKID_CONNREQ and illegal negative domain return ""; std::ostringstream os; @@ -237,12 +237,12 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -int srt::CUDTUnited::startup() +SRTSTATUS srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return 1; + return SRTSTATUS(1); // Global initialization code #ifdef _WIN32 @@ -259,21 +259,21 @@ int srt::CUDTUnited::startup() PacketFilter::globalInit(); if (m_bGCStatus) - return 1; + return SRTSTATUS(1); m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) - return -1; + return SRT_ERROR; m_bGCStatus = true; HLOGC(inlog.Debug, log << "SRT Clock Type: " << SRT_SYNC_CLOCK_STR); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::cleanup() +SRTSTATUS srt::CUDTUnited::cleanup() { // IMPORTANT!!! // In this function there must be NO LOGGING AT ALL. This function may @@ -286,10 +286,10 @@ int srt::CUDTUnited::cleanup() ScopedLock gcinit(m_InitLock); if (--m_iInstanceCount > 0) - return 0; + return SRT_STATUS_OK; if (!m_bGCStatus) - return 0; + return SRT_STATUS_OK; { UniqueLock gclock(m_GCStopLock); @@ -310,7 +310,7 @@ int srt::CUDTUnited::cleanup() WSACleanup(); #endif - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) @@ -360,10 +360,10 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) const bool exists = #if ENABLE_BONDING for_group - ? m_Groups.count(sockval | SRTGROUP_MASK) + ? m_Groups.count(SRTSOCKET(sockval | SRTGROUP_MASK)) : #endif - m_Sockets.count(sockval); + m_Sockets.count(SRTSOCKET(sockval)); leaveCS(m_GlobControlLock); if (exists) @@ -412,7 +412,7 @@ SRTSOCKET srt::CUDTUnited::generateSocketID(bool for_group) LOGC(smlog.Debug, log << "generateSocketID: " << (for_group ? "(group)" : "") << ": @" << sockval); - return sockval; + return SRTSOCKET(sockval); } SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) @@ -442,7 +442,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) throw; } ns->m_Status = SRTS_INIT; - ns->m_ListenSocket = 0; + ns->m_ListenSocket = SRT_SOCKID_CONNREQ; // A value used for socket if it wasn't listener-spawned ns->core().m_SocketID = ns->m_SocketID; ns->core().m_pCache = m_pCache; @@ -470,7 +470,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } -int srt::CUDTUnited::newConnection(const SRTSOCKET listen, +int srt::CUDTUnited::newConnection(const SRTSOCKET listener, const sockaddr_any& peer, const CPacket& hspkt, CHandShake& w_hs, @@ -484,15 +484,15 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // Can't manage this error through an exception because this is // running in the listener loop. - CUDTSocket* ls = locateSocket(listen); + CUDTSocket* ls = locateSocket(listener); if (!ls) { - LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listen << " which DOES NOT EXIST."); + LOGC(cnlog.Error, log << "IPE: newConnection by listener socket id=" << listener << " which DOES NOT EXIST."); return -1; } HLOGC(cnlog.Debug, - log << "newConnection: creating new socket after listener @" << listen + log << "newConnection: creating new socket after listener @" << listener << " contacted with backlog=" << ls->m_uiBackLog); // if this connection has already been processed @@ -572,13 +572,13 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, return -1; } - ns->m_ListenSocket = listen; + ns->m_ListenSocket = listener; ns->core().m_SocketID = ns->m_SocketID; ns->m_PeerID = w_hs.m_iID; ns->m_iISN = w_hs.m_iISN; HLOGC(cnlog.Debug, - log << "newConnection: DATA: lsnid=" << listen << " id=" << ns->core().m_SocketID + log << "newConnection: DATA: lsnid=" << listener << " id=" << ns->core().m_SocketID << " peerid=" << ns->core().m_PeerID << " ISN=" << ns->m_iISN); int error = 0; @@ -769,7 +769,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, HLOGC(cnlog.Debug, log << "ACCEPT: new socket @" << ns->m_SocketID << " submitted for acceptance"); // acknowledge users waiting for new connections on the listening socket - m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, true); CGlobEvent::triggerEvent(); @@ -790,7 +790,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, // acknowledge INTERNAL users waiting for new connections on the listening socket // that are reported when a new socket is connected within an already connected group. - m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); + m_EPoll.update_events(listener, ls->core().m_sPollID, SRT_EPOLL_UPDATE, true); CGlobEvent::triggerEvent(); } @@ -838,12 +838,12 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // static forwarder -int srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { return uglobal().installAcceptHook(lsn, hook, opaq); } -int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { try { @@ -856,24 +856,24 @@ int srt::CUDTUnited::installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } -int srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDT::installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { return uglobal().installConnectHook(lsn, hook, opaq); } -int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_fn* hook, void* opaq) { try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->installConnectHook(hook, opaq); - return 0; + return SRT_STATUS_OK; } #endif CUDTSocket* s = locateSocket(u, ERH_THROW); @@ -885,7 +885,7 @@ int srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_callback_ return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) @@ -905,7 +905,7 @@ SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } -int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -920,10 +920,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr)); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) +SRTSTATUS srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) { ScopedLock cg(s->m_ControlLock); @@ -949,10 +949,10 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // copy address information of local node s->core().m_pSndQueue->m_pChannel->getSockAddr(s->m_SelfAddr); - return 0; + return SRT_STATUS_OK; } -int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) { if (backlog <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -975,7 +975,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // do nothing if the socket is already listening if (s->m_Status == SRTS_LISTENING) - return 0; + return SRT_STATUS_OK; // a socket can listen only if is in OPENED status if (s->m_Status != SRTS_OPENED) @@ -995,7 +995,7 @@ int srt::CUDTUnited::listen(const SRTSOCKET u, int backlog) // if thrown, remains in OPENED state if so. s->m_Status = SRTS_LISTENING; - return 0; + return SRT_STATUS_OK; } SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut) @@ -1037,7 +1037,7 @@ SRTSOCKET srt::CUDTUnited::accept_bond(const SRTSOCKET listeners[], int lsize, i // Theoretically we can have a situation that more than one // listener is ready for accept. In this case simply get // only the first found. - int lsn = st.begin()->first; + SRTSOCKET lsn = st.begin()->first; sockaddr_storage dummy; int outlen = sizeof dummy; return accept(lsn, ((sockaddr*)&dummy), (&outlen)); @@ -1077,7 +1077,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int throw CUDTException(MJ_NOTSUP, MN_NOLISTEN, 0); } - SRTSOCKET u = CUDT::INVALID_SOCK; + SRTSOCKET u = SRT_INVALID_SOCK; bool accepted = false; // !!only one connection can be set up each time!! @@ -1110,7 +1110,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int m_EPoll.update_events(listen, ls->core().m_sPollID, SRT_EPOLL_ACCEPT, false); } - if (u == CUDT::INVALID_SOCK) + if (u == SRT_INVALID_SOCK) { // non-blocking receiving, no connection available if (!ls->core().m_config.bSynRecving) @@ -1178,7 +1178,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int return u; } -int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) +SRTSOCKET srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified if (!srcname || !tarname || namelen < int(sizeof(sockaddr_in))) @@ -1200,7 +1200,7 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); // Note: forced_isn is ignored when connecting a group. @@ -1221,10 +1221,11 @@ int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockadd // For a single socket, just do bind, then connect bind(s, source_addr); - return connectIn(s, target_addr, SRT_SEQNO_NONE); + connectIn(s, target_addr, SRT_SEQNO_NONE); + return SRT_SOCKID_CONNREQ; } -int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { if (!name || namelen < int(sizeof(sockaddr_in))) { @@ -1240,7 +1241,7 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); @@ -1257,31 +1258,29 @@ int srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int namele if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - return connectIn(s, target_addr, forced_isn); + connectIn(s, target_addr, forced_isn); + return SRT_SOCKID_CONNREQ; } #if ENABLE_BONDING -int srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) +SRTSOCKET srt::CUDTUnited::singleMemberConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* gd) { - int gstat = groupConnect(pg, gd, 1); - if (gstat == -1) + SRTSOCKET gstat = groupConnect(pg, gd, 1); + if (gstat == SRT_INVALID_SOCK) { // We have only one element here, so refer to it. // Sanity check if (gd->errorcode == SRT_SUCCESS) gd->errorcode = SRT_EINVPARAM; - CodeMajor mj = CodeMajor(gd->errorcode / 1000); - CodeMinor mn = CodeMinor(gd->errorcode % 1000); - - return CUDT::APIError(mj, mn); + return CUDT::APIError(gd->errorcode), SRT_INVALID_SOCK; } return gstat; } // [[using assert(pg->m_iBusy > 0)]] -int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) +SRTSOCKET srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, int arraysize) { CUDTGroup& g = *pg; SRT_ASSERT(g.m_iBusy > 0); @@ -1323,7 +1322,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // This also should happen if ERR flag was set, as IN and OUT could be set, too. m_EPoll.update_events(g.id(), g.m_sPollID, SRT_EPOLL_IN | SRT_EPOLL_OUT, false); } - SRTSOCKET retval = -1; + SRTSOCKET retval = SRT_INVALID_SOCK; int eid = -1; int connect_modes = SRT_EPOLL_CONNECT | SRT_EPOLL_ERR; @@ -1470,7 +1469,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } else { - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; delete ns; m_Sockets.erase(sid); @@ -1532,7 +1531,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // to avoid locking more than one mutex at a time. erc_rloc = e.getErrorCode(); targets[tii].errorcode = e.getErrorCode(); - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); @@ -1545,7 +1544,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i { LOGC(aclog.Fatal, log << "groupConnect: IPE: UNKNOWN EXCEPTION from connectIn"); targets[tii].errorcode = SRT_ESYSOBJ; - targets[tii].id = CUDT::INVALID_SOCK; + targets[tii].id = SRT_INVALID_SOCK; ScopedLock cl(m_GlobControlLock); ns->removeFromGroup(false); m_Sockets.erase(ns->m_SocketID); @@ -1594,7 +1593,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // set BROKEN or PENDING. f->sndstate = SRT_GST_PENDING; f->rcvstate = SRT_GST_PENDING; - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -1642,7 +1641,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } } - if (retval == -1) + if (retval == SRT_INVALID_SOCK) { HLOGC(aclog.Debug, log << "groupConnect: none succeeded as background-spawn, exit with error"); block_new_opened = false; // Avoid executing further while loop @@ -1655,13 +1654,13 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i if (spawned.empty()) { // All were removed due to errors. - retval = -1; + retval = SRT_INVALID_SOCK; break; } HLOGC(aclog.Debug, log << "groupConnect: first connection, applying EPOLL WAITING."); int len = (int)spawned.size(); vector ready(spawned.size()); - const int estat = srt_epoll_wait(eid, + const int estat = srt_epoll_wait(eid, NULL, NULL, // IN/ACCEPT &ready[0], @@ -1673,7 +1672,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i NULL); // Sanity check. Shouldn't happen if subs are in sync with spawned. - if (estat == -1) + if (estat == int(SRT_ERROR)) { #if ENABLE_LOGGING CUDTException& x = CUDT::getlasterror(); @@ -1685,7 +1684,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i } #endif HLOGC(aclog.Debug, log << "groupConnect: srt_epoll_wait failed - breaking the wait loop"); - retval = -1; + retval = SRT_INVALID_SOCK; break; } @@ -1807,14 +1806,14 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i // standing. Each one could, however, break by a different reason, // for example, one by timeout, another by wrong passphrase. Check // the `errorcode` field to determine the reaon for particular link. - if (retval == -1) + if (retval == SRT_INVALID_SOCK) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); return retval; } #endif -int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) +void srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, int32_t forced_isn) { ScopedLock cg(s->m_ControlLock); // a socket can "connect" only if it is in the following states: @@ -1876,19 +1875,17 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i s->m_Status = SRTS_OPENED; throw; } - - return 0; } -int srt::CUDTUnited::close(const SRTSOCKET u) +SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->close(); deleteGroup(k.group); - return 0; + return SRT_STATUS_OK; } #endif CUDTSocket* s = locateSocket(u); @@ -1946,7 +1943,7 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int srt::CUDTUnited::close(CUDTSocket* s) +SRTSTATUS srt::CUDTUnited::close(CUDTSocket* s) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); @@ -1959,7 +1956,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if (s->m_Status == SRTS_LISTENING) { if (s->core().m_bBroken) - return 0; + return SRT_STATUS_OK; s->m_tsClosureTimeStamp = steady_clock::now(); s->core().m_bBroken = true; @@ -2007,7 +2004,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) if ((i == m_Sockets.end()) || (i->second->m_Status == SRTS_CLOSED)) { HLOGC(smlog.Debug, log << "@" << u << "U::close: NOT AN ACTIVE SOCKET, returning."); - return 0; + return SRT_STATUS_OK; } s = i->second; s->setClosed(); @@ -2104,7 +2101,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } */ - return 0; + return SRT_STATUS_OK; } void srt::CUDTUnited::getpeername(const SRTSOCKET u, sockaddr* pw_name, int* pw_namelen) @@ -2330,59 +2327,55 @@ int srt::CUDTUnited::epoll_create() return m_EPoll.create(); } -int srt::CUDTUnited::epoll_clear_usocks(int eid) +void srt::CUDTUnited::epoll_clear_usocks(int eid) { return m_EPoll.clear_usocks(eid); } -int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +void srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { - int ret = -1; #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); - ret = m_EPoll.update_usock(eid, u, events); + m_EPoll.update_usock(eid, u, events); k.group->addEPoll(eid); - return 0; + return; } #endif CUDTSocket* s = locateSocket(u); if (s) { - ret = epoll_add_usock_INTERNAL(eid, s, events); + epoll_add_usock_INTERNAL(eid, s, events); } else { throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); } - - return ret; } // NOTE: WILL LOCK (serially): // - CEPoll::m_EPollLock // - CUDT::m_RecvLock -int srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) +void srt::CUDTUnited::epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events) { - int ret = m_EPoll.update_usock(eid, s->m_SocketID, events); + m_EPoll.update_usock(eid, s->m_SocketID, events); s->core().addEPoll(eid); - return ret; } -int srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.add_ssock(eid, s, events); } -int srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +void srt::CUDTUnited::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { return m_EPoll.update_ssock(eid, s, events); } template -int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) +void srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) { // XXX Not sure if this is anyhow necessary because setting readiness // to false doesn't actually trigger any action. Further research needed. @@ -2398,31 +2391,29 @@ int srt::CUDTUnited::epoll_remove_entity(const int eid, EntityType* ent) HLOGC(ealog.Debug, log << "epoll_remove_usock: CLEARING subscription on E" << eid << " of @" << ent->id()); int no_events = 0; - int ret = m_EPoll.update_usock(eid, ent->id(), &no_events); - - return ret; + m_EPoll.update_usock(eid, ent->id(), &no_events); } // Needed internal access! -int srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) +void srt::CUDTUnited::epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* s) { return epoll_remove_entity(eid, &s->core()); } #if ENABLE_BONDING -int srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) +void srt::CUDTUnited::epoll_remove_group_INTERNAL(const int eid, CUDTGroup* g) { return epoll_remove_entity(eid, g); } #endif -int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) +void srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) { CUDTSocket* s = 0; #if ENABLE_BONDING CUDTGroup* g = 0; - if (u & SRTGROUP_MASK) + if (isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); g = k.group; @@ -2442,7 +2433,7 @@ int srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) return m_EPoll.update_usock(eid, u, &no_events); } -int srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) +void srt::CUDTUnited::epoll_remove_ssock(const int eid, const SYSSOCKET s) { return m_EPoll.remove_ssock(eid, s); } @@ -2457,7 +2448,7 @@ int32_t srt::CUDTUnited::epoll_set(int eid, int32_t flags) return m_EPoll.setflags(eid, flags); } -int srt::CUDTUnited::epoll_release(const int eid) +void srt::CUDTUnited::epoll_release(const int eid) { return m_EPoll.release(eid); } @@ -2626,18 +2617,21 @@ void srt::CUDTUnited::checkBrokenSockets() tbc.push_back(i->first); m_ClosedSockets[i->first] = s; - // remove from listener's queue - sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); - if (ls == m_Sockets.end()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = m_ClosedSockets.find(s->m_ListenSocket); - if (ls == m_ClosedSockets.end()) - continue; - } + // remove from listener's queue + sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); + if (ls == m_Sockets.end()) + { + ls = m_ClosedSockets.find(s->m_ListenSocket); + if (ls == m_ClosedSockets.end()) + continue; + } - enterCS(ls->second->m_AcceptLock); - ls->second->m_QueuedSockets.erase(s->m_SocketID); - leaveCS(ls->second->m_AcceptLock); + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); + } } for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) @@ -2810,7 +2804,7 @@ void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int w_m.m_mcfg = s->core().m_config; w_m.m_iIPversion = af; w_m.m_iRefCount = 1; - w_m.m_iID = s->m_SocketID; + w_m.m_iID = int32_t(s->m_SocketID); } uint16_t srt::CUDTUnited::installMuxer(CUDTSocket* w_s, CMultiplexer& fw_sm) @@ -3133,18 +3127,21 @@ void* srt::CUDTUnited::garbageCollect(void* p) #endif self->m_ClosedSockets[i->first] = s; - // remove from listener's queue - sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); - if (ls == self->m_Sockets.end()) + if (s->m_ListenSocket != SRT_SOCKID_CONNREQ) { - ls = self->m_ClosedSockets.find(s->m_ListenSocket); - if (ls == self->m_ClosedSockets.end()) - continue; - } + // remove from listener's queue + sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); + if (ls == self->m_Sockets.end()) + { + ls = self->m_ClosedSockets.find(s->m_ListenSocket); + if (ls == self->m_ClosedSockets.end()) + continue; + } - enterCS(ls->second->m_AcceptLock); - ls->second->m_QueuedSockets.erase(s->m_SocketID); - leaveCS(ls->second->m_AcceptLock); + enterCS(ls->second->m_AcceptLock); + ls->second->m_QueuedSockets.erase(s->m_SocketID); + leaveCS(ls->second->m_AcceptLock); + } } self->m_Sockets.clear(); @@ -3176,12 +3173,12 @@ void* srt::CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -int srt::CUDT::startup() +SRTSTATUS srt::CUDT::startup() { return uglobal().startup(); } -int srt::CUDT::cleanup() +SRTSTATUS srt::CUDT::cleanup() { return uglobal().cleanup(); } @@ -3197,19 +3194,19 @@ SRTSOCKET srt::CUDT::socket() } catch (const CUDTException& e) { - SetThreadLocalError(e); - return INVALID_SOCK; + APIError a(e); + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { - SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + APIError a(MJ_SYSTEMRES, MN_MEMORY, 0); + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "socket: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + APIError a(MJ_UNKNOWN, MN_NONE, 0); + return SRT_INVALID_SOCK; } } @@ -3223,6 +3220,13 @@ srt::CUDT::APIError::APIError(CodeMajor mj, CodeMinor mn, int syserr) SetThreadLocalError(CUDTException(mj, mn, syserr)); } +srt::CUDT::APIError::APIError(int errorcode) +{ + CodeMajor mj = CodeMajor(errorcode / 1000); + CodeMinor mn = CodeMinor(errorcode % 1000); + SetThreadLocalError(CUDTException(mj, mn, 0)); +} + #if ENABLE_BONDING // This is an internal function; 'type' should be pre-checked if it has a correct value. // This doesn't have argument of GroupType due to header file conflicts. @@ -3254,14 +3258,14 @@ SRTSOCKET srt::CUDT::createGroup(SRT_GROUP_TYPE gt) } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (...) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } - return SRT_INVALID_SOCK; + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } // [[using locked(m_ControlLock)]] @@ -3303,14 +3307,14 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) ScopedLock glock(uglobal().m_GlobControlLock); CUDTSocket* s = uglobal().locateSocket_LOCKED(socket); if (!s || !s->m_GroupOf) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; return s->m_GroupOf->id(); } int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { - if ((groupid & SRTGROUP_MASK) == 0 || !psize) + if (!isgroup(groupid) || !psize) { return APIError(MJ_NOTSUP, MN_INVAL, 0); } @@ -3326,7 +3330,7 @@ int srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* } #endif -int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) { try { @@ -3360,7 +3364,7 @@ int srt::CUDT::bind(SRTSOCKET u, const sockaddr* name, int namelen) } } -int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) +SRTSTATUS srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) { try { @@ -3385,7 +3389,7 @@ int srt::CUDT::bind(SRTSOCKET u, UDPSOCKET udpsock) } } -int srt::CUDT::listen(SRTSOCKET u, int backlog) +SRTSTATUS srt::CUDT::listen(SRTSOCKET u, int backlog) { try { @@ -3415,18 +3419,18 @@ SRTSOCKET srt::CUDT::accept_bond(const SRTSOCKET listeners[], int lsize, int64_t catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept_bond: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } @@ -3439,22 +3443,22 @@ SRTSOCKET srt::CUDT::accept(SRTSOCKET u, sockaddr* addr, int* addrlen) catch (const CUDTException& e) { SetThreadLocalError(e); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const bad_alloc&) { SetThreadLocalError(CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "accept: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return INVALID_SOCK; + return SRT_INVALID_SOCK; } } -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen) { try { @@ -3462,29 +3466,29 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #if ENABLE_BONDING -int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) +SRTSOCKET srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int arraysize) { if (arraysize <= 0) - return APIError(MJ_NOTSUP, MN_INVAL, 0); + return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; - if ((grp & SRTGROUP_MASK) == 0) + if (!isgroup(grp)) { // connectLinks accepts only GROUP id, not socket id. - return APIError(MJ_NOTSUP, MN_SIDINVAL, 0); + return APIError(MJ_NOTSUP, MN_SIDINVAL, 0), SRT_INVALID_SOCK; } try @@ -3494,21 +3498,21 @@ int srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], int ar } catch (CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } #endif -int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) +SRTSOCKET srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn) { try { @@ -3516,20 +3520,20 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t f } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), SRT_INVALID_SOCK; } catch (bad_alloc&) { - return APIError(MJ_SYSTEMRES, MN_MEMORY, 0); + return APIError(MJ_SYSTEMRES, MN_MEMORY, 0), SRT_INVALID_SOCK; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "connect: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), SRT_INVALID_SOCK; } } -int srt::CUDT::close(SRTSOCKET u) +SRTSTATUS srt::CUDT::close(SRTSOCKET u) { try { @@ -3546,12 +3550,12 @@ int srt::CUDT::close(SRTSOCKET u) } } -int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getpeername(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3564,12 +3568,12 @@ int srt::CUDT::getpeername(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) +SRTSTATUS srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) { try { uglobal().getsockname(u, name, namelen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3582,7 +3586,7 @@ int srt::CUDT::getsockname(SRTSOCKET u, sockaddr* name, int* namelen) } } -int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) +SRTSTATUS srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval, int* pw_optlen) { if (!pw_optval || !pw_optlen) { @@ -3592,17 +3596,17 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.getOpt(optname, (pw_optval), (*pw_optlen)); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3615,7 +3619,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval } } -int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) +SRTSTATUS srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { if (!optval) return APIError(MJ_NOTSUP, MN_INVAL, 0); @@ -3623,17 +3627,17 @@ int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* opt try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } #endif CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.setOpt(optname, optval, optlen); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3668,7 +3672,7 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); @@ -3712,7 +3716,7 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); @@ -3844,11 +3848,12 @@ int srt::CUDT::epoll_create() } } -int srt::CUDT::epoll_clear_usocks(int eid) +SRTSTATUS srt::CUDT::epoll_clear_usocks(int eid) { try { - return uglobal().epoll_clear_usocks(eid); + uglobal().epoll_clear_usocks(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3862,11 +3867,12 @@ int srt::CUDT::epoll_clear_usocks(int eid) } } -int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3879,11 +3885,12 @@ int srt::CUDT::epoll_add_usock(const int eid, const SRTSOCKET u, const int* even } } -int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_add_ssock(eid, s, events); + uglobal().epoll_add_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3896,11 +3903,12 @@ int srt::CUDT::epoll_add_ssock(const int eid, const SYSSOCKET s, const int* even } } -int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) +SRTSTATUS srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* events) { try { - return uglobal().epoll_add_usock(eid, u, events); + uglobal().epoll_add_usock(eid, u, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3914,11 +3922,12 @@ int srt::CUDT::epoll_update_usock(const int eid, const SRTSOCKET u, const int* e } } -int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) +SRTSTATUS srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events) { try { - return uglobal().epoll_update_ssock(eid, s, events); + uglobal().epoll_update_ssock(eid, s, events); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3932,11 +3941,12 @@ int srt::CUDT::epoll_update_ssock(const int eid, const SYSSOCKET s, const int* e } } -int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) +SRTSTATUS srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) { try { - return uglobal().epoll_remove_usock(eid, u); + uglobal().epoll_remove_usock(eid, u); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3950,11 +3960,12 @@ int srt::CUDT::epoll_remove_usock(const int eid, const SRTSOCKET u) } } -int srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) +SRTSTATUS srt::CUDT::epoll_remove_ssock(const int eid, const SYSSOCKET s) { try { - return uglobal().epoll_remove_ssock(eid, s); + uglobal().epoll_remove_ssock(eid, s); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -3998,12 +4009,12 @@ int srt::CUDT::epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, } catch (const CUDTException& e) { - return APIError(e); + return APIError(e), int(SRT_ERROR); } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "epoll_uwait: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); - return APIError(MJ_UNKNOWN, MN_NONE, 0); + return APIError(MJ_UNKNOWN, MN_NONE, 0), int(SRT_ERROR); } } @@ -4024,11 +4035,12 @@ int32_t srt::CUDT::epoll_set(const int eid, int32_t flags) } } -int srt::CUDT::epoll_release(const int eid) +SRTSTATUS srt::CUDT::epoll_release(const int eid) { try { - return uglobal().epoll_release(eid); + uglobal().epoll_release(eid); + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4046,10 +4058,10 @@ srt::CUDTException& srt::CUDT::getlasterror() return GetThreadLocalError(); } -int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) +SRTSTATUS srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_BONDING - if (u & SRTGROUP_MASK) + if (isgroup(u)) return groupsockbstats(u, perf, clear); #endif @@ -4057,7 +4069,7 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta { CUDT& udt = uglobal().locateSocket(u, CUDTUnited::ERH_THROW)->core(); udt.bstats(perf, clear, instantaneous); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { @@ -4071,24 +4083,24 @@ int srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instanta } #if ENABLE_BONDING -int srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) +SRTSTATUS srt::CUDT::groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear) { try { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->bstatsSocket(perf, clear); - return 0; + return SRT_STATUS_OK; } catch (const CUDTException& e) { SetThreadLocalError(e); - return ERROR; + return SRT_ERROR; } catch (const std::exception& ee) { LOGC(aclog.Fatal, log << "bstats: UNEXPECTED EXCEPTION: " << typeid(ee).name() << ": " << ee.what()); SetThreadLocalError(CUDTException(MJ_UNKNOWN, MN_NONE, 0)); - return ERROR; + return SRT_ERROR; } } #endif @@ -4525,7 +4537,7 @@ int getrejectreason(SRTSOCKET u) return CUDT::rejectReason(u); } -int setrejectreason(SRTSOCKET u, int value) +SRTSTATUS setrejectreason(SRTSOCKET u, int value) { return CUDT::rejectReason(u, value); } diff --git a/srtcore/api.h b/srtcore/api.h index 551590e3e..9477ebb28 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -84,7 +84,7 @@ class CUDTSocket CUDTSocket() : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -103,7 +103,7 @@ class CUDTSocket CUDTSocket(const CUDTSocket& ancestor) : m_Status(SRTS_INIT) , m_SocketID(0) - , m_ListenSocket(0) + , m_ListenSocket(SRT_SOCKID_CONNREQ) , m_PeerID(0) #if ENABLE_BONDING , m_GroupMemberData() @@ -245,11 +245,11 @@ class CUDTUnited /// initialize the UDT library. /// @return 0 if success, otherwise -1 is returned. - int startup(); + SRTSTATUS startup(); /// release the UDT library. /// @return 0 if success, otherwise -1 is returned. - int cleanup(); + SRTSTATUS cleanup(); /// Create a new UDT socket. /// @param [out] pps Variable (optional) to which the new socket will be written, if succeeded @@ -273,8 +273,8 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); - int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); - int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); + SRTSTATUS installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + SRTSTATUS installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); /// Check the status of the UDT socket. /// @param [in] u the UDT socket ID. @@ -283,20 +283,20 @@ class CUDTUnited // socket APIs - int bind(CUDTSocket* u, const sockaddr_any& name); - int bind(CUDTSocket* u, UDPSOCKET udpsock); - int listen(const SRTSOCKET u, int backlog); + SRTSTATUS bind(CUDTSocket* u, const sockaddr_any& name); + SRTSTATUS bind(CUDTSocket* u, UDPSOCKET udpsock); + SRTSTATUS listen(const SRTSOCKET u, int backlog); SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen); SRTSOCKET accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); - int connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); - int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - int connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); + SRTSOCKET connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen); + SRTSOCKET connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + void connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn); #if ENABLE_BONDING - int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); - int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); + SRTSOCKET groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); + SRTSOCKET singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); #endif - int close(const SRTSOCKET u); - int close(CUDTSocket* s); + SRTSTATUS close(const SRTSOCKET u); + SRTSTATUS close(CUDTSocket* s); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); @@ -306,22 +306,22 @@ class CUDTUnited std::vector* exceptfds, int64_t msTimeOut); int epoll_create(); - int epoll_clear_usocks(int eid); - int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - int epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); - int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_remove_usock(const int eid, const SRTSOCKET u); + void epoll_clear_usocks(int eid); + void epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + void epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events); + void epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + void epoll_remove_usock(const int eid, const SRTSOCKET u); template - int epoll_remove_entity(const int eid, EntityType* ent); - int epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); + void epoll_remove_entity(const int eid, EntityType* ent); + void epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent); #if ENABLE_BONDING - int epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); + void epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent); #endif - int epoll_remove_ssock(const int eid, const SYSSOCKET s); - int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); + void epoll_remove_ssock(const int eid, const SYSSOCKET s); + void epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); int32_t epoll_set(const int eid, int32_t flags); - int epoll_release(const int eid); + void epoll_release(const int eid); #if ENABLE_BONDING // [[using locked(m_GlobControlLock)]] @@ -396,8 +396,8 @@ class CUDTUnited sync::Mutex m_IDLock; // used to synchronize ID generation - SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID - SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one + int32_t m_SocketIDGenerator; // seed to generate a new unique socket ID + int32_t m_SocketIDGenerator_init; // Keeps track of the very first one std::map > m_PeerRec; // record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a7056c3b2..dcc585a75 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -262,6 +262,10 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosInc); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { + if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. + { + HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with mflag field set to a negative value!"); + } IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; int iDropCnt = 0; diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index e945b166c..7e55ecad4 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -320,7 +320,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -346,7 +346,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -421,9 +421,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, Drop& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -438,19 +439,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -462,11 +467,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -476,18 +481,21 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); + SRT_ASSERT(w_drop.seqno[Drop::END] == p->m_iSeqNo); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; @@ -500,7 +508,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -508,7 +516,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5dce96ae0..d0dcdd453 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct Drop + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. + /// @param [in,out] packet the packet to read. /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, Drop& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 30fd98778..4f752f290 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -613,8 +613,8 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { HLOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f1f497ecb..3cf9fdaad 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -88,8 +88,8 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; -const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; -const int UDT::ERROR = srt::CUDT::ERROR; +const SRTSOCKET UDT::INVALID_SOCK = SRT_INVALID_SOCK; +const SRTSTATUS UDT::ERROR = SRT_ERROR; //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -272,7 +272,7 @@ void srt::CUDT::construct() // Will be reset to 0 for HSv5, this value is important for HSv4. m_iSndHsRetryCnt = SRT_MAX_HSRETRY + 1; - m_PeerID = 0; + m_PeerID = SRT_SOCKID_CONNREQ; m_bOpened = false; m_bListening = false; m_bConnecting = false; @@ -1960,7 +1960,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2491,7 +2491,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2518,7 +2518,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3034,7 +3034,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // for consistency and possibly changes in future. // We are granted these two fields do exist - SRTSOCKET grpid = groupdata[GRPD_GROUPID]; + SRTSOCKET grpid = SRTSOCKET(groupdata[GRPD_GROUPID]); uint32_t gd = groupdata[GRPD_GROUPDATA]; SRT_GROUP_TYPE gtp = SRT_GROUP_TYPE(SrtHSRequest::HS_GROUP_TYPE::unwrap(gd)); @@ -3057,7 +3057,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A return false; } - if ((grpid & SRTGROUP_MASK) == 0) + if (!isgroup(grpid)) { m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS/GROUP: socket ID passed as a group ID is not a group ID"); @@ -3127,7 +3127,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A } SRTSOCKET peer = pg->peerid(); - if (peer == -1) + if (peer == SRT_INVALID_SOCK) { // This is the first connection within this group, so this group // has just been informed about the peer membership. Accept it. @@ -3169,10 +3169,10 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A // ID to the peer. SRTSOCKET lgid = makeMePeerOf(grpid, gtp, link_flags); - if (!lgid) + if (lgid == SRT_SOCKID_CONNREQ) return true; // already done - if (lgid == -1) + if (lgid == SRT_INVALID_SOCK) { // NOTE: This error currently isn't reported by makeMePeerOf, // so this is left to handle a possible error introduced in future. @@ -3229,7 +3229,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp << " agent group=$" << gp->id() << " type" << gp->type()); - return -1; + return SRT_INVALID_SOCK; } HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: group for peer=$" << peergroup << " found: $" << gp->id()); @@ -3246,14 +3246,14 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 catch (...) { // Expected exceptions are only those referring to system resources - return -1; + return SRT_INVALID_SOCK; } if (!gp->applyFlags(link_flags, m_SrtHsSide)) { // Wrong settings. Must reject. Delete group. uglobal().deleteGroup_LOCKED(gp); - return -1; + return SRT_INVALID_SOCK; } gp->set_peerid(peergroup); @@ -3297,7 +3297,7 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 LOGC(gmlog.Error, log << CONID() << "IPE (non-fatal): the socket is in the group, but has no clue about it!"); s->m_GroupOf = gp; s->m_GroupMemberData = f; - return 0; + return SRT_SOCKID_CONNREQ; } s->m_GroupMemberData = gp->add(groups::prepareSocketData(s)); @@ -3520,7 +3520,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(SRT_SOCKID_CONNREQ); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3535,7 +3535,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3600,7 +3600,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3818,17 +3818,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? SRT_SOCKID_CONNREQ : m_ConnRes.m_iID); bool status = true; @@ -3839,7 +3839,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3871,8 +3871,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3884,7 +3884,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3894,16 +3894,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request); + m_pSndQueue->sendto(serv_addr, reqpkt); return status; } @@ -5674,15 +5674,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5693,10 +5693,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show()); } #endif @@ -5706,7 +5706,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } // This function is required to be called when a caller receives an INDUCTION @@ -5912,7 +5912,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -6740,7 +6740,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ return 0; // Forced to return error instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0), int(SRT_ERROR); throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); } else @@ -6879,7 +6879,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ { // Forced to return 0 instead of throwing exception. if (!by_exception) - return APIError(MJ_CONNECTION, MN_CONNLOST, 0); + return APIError(MJ_CONNECTION, MN_CONNLOST, 0), int(SRT_ERROR); if (!m_config.bMessageAPI && m_bShutdown) return 0; throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); @@ -7620,8 +7620,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + ctrlpkt.set_id(m_PeerID); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7635,7 +7635,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7656,7 +7656,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7686,7 +7686,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); m_tsLastWarningTime = steady_clock::now(); @@ -7695,37 +7695,37 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_SHUTDOWN: // 101 - Shutdown - if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. + if (m_PeerID == SRT_SOCKID_CONNREQ) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7812,7 +7812,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8019,7 +8019,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8711,18 +8711,18 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) + (rsppkt), (initdata))) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -8737,6 +8737,27 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) { + typedef int32_t expected_t[2]; + if (ctrlpkt.getLength() < sizeof (expected_t)) + { + // We ALLOW packets that are bigger than this to allow + // future extensions, this just interprets the part that + // is expected, and reject only those that don't carry + // even the required data. + LOGC(brlog.Error, log << CONID() << "EPE: Wrong size of the DROPREQ message: " << ctrlpkt.getLength() + << " - expected >=" << sizeof(expected_t)); + return; + } + + int32_t msgno = ctrlpkt.getMsgSeq(m_bPeerRexmitFlag); + + // Check for rogue message + if (msgno == SRT_MSGNO_NONE) + { + LOGC(brlog.Warn, log << CONID() << "ROGUE DROPREQ detected with #NONE - fallback: fixing to #CONTROL"); + msgno = SRT_MSGNO_CONTROL; + } + const int32_t* dropdata = (const int32_t*) ctrlpkt.m_pcData; { @@ -8747,15 +8768,13 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) // Still remove the record from the loss list to cease further retransmission requests. if (!m_bTLPktDrop || !m_bTsbPd) { - const bool using_rexmit_flag = m_bPeerRexmitFlag; ScopedLock rblock(m_RcvBufferLock); - const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], ctrlpkt.getMsgSeq(using_rexmit_flag)); + const int iDropCnt = m_pRcvBuffer->dropMessage(dropdata[0], dropdata[1], msgno); if (iDropCnt > 0) { LOGC(brlog.Warn, log << CONID() << "RCV-DROPPED " << iDropCnt << " packet(s), seqno range %" - << dropdata[0] << "-%" << dropdata[1] << ", msgno " << ctrlpkt.getMsgSeq(using_rexmit_flag) - << " (SND DROP REQUEST)."); + << dropdata[0] << "-%" << dropdata[1] << ", #" << msgno << " (SND DROP REQUEST)."); enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. @@ -8844,7 +8863,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=@" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9019,37 +9038,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9059,35 +9085,32 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; } } - int msglen; + CSndBuffer::Drop buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[CSndBuffer::Drop::BEGIN], buffer_drop.seqno[CSndBuffer::Drop::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[CSndBuffer::Drop::BEGIN] << " - " + << buffer_drop.seqno[CSndBuffer::Drop::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[CSndBuffer::Drop::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[CSndBuffer::Drop::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored @@ -9112,7 +9135,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9213,7 +9236,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9225,14 +9248,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9241,7 +9264,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9338,14 +9361,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9355,7 +9378,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9374,7 +9397,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9460,7 +9483,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9474,7 +9497,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9482,7 +9505,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9491,7 +9514,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9502,17 +9525,17 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // packet are not present in the buffer (preadte the send buffer). // Override extraction sequence with scheduling sequence. - m_iSndCurrSeqNo = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); ScopedLock ackguard(m_RecvAckLock); - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << m_iSndCurrSeqNo << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9524,15 +9547,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = m_iSndCurrSeqNo; + w_packet.set_seqno(m_iSndCurrSeqNo); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9552,7 +9575,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -9723,7 +9746,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -9748,7 +9771,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -9761,7 +9784,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -9783,7 +9806,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -9794,7 +9817,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -9885,7 +9908,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -9899,12 +9922,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -9912,9 +9935,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -9962,7 +9985,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10006,7 +10029,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10032,7 +10055,7 @@ int srt::CUDT::processData(CUnit* in_unit) // supply the missing packet(s), and the loss will no longer be visible for the code that follows. if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing { - const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10048,13 +10071,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10120,7 +10143,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10405,7 +10428,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10425,7 +10448,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10721,7 +10744,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -10789,7 +10812,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -10837,8 +10860,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } @@ -10949,7 +10972,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp); } else @@ -10960,7 +10983,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); @@ -11448,7 +11471,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u) return s->core().m_RejectReason; } -int srt::CUDT::rejectReason(SRTSOCKET u, int value) +SRTSTATUS srt::CUDT::rejectReason(SRTSOCKET u, int value) { CUDTSocket* s = uglobal().locateSocket(u); if (!s) @@ -11458,7 +11481,7 @@ int srt::CUDT::rejectReason(SRTSOCKET u, int value) return APIError(MJ_NOTSUP, MN_INVAL); s->core().m_RejectReason = value; - return 0; + return SRT_STATUS_OK; } int64_t srt::CUDT::socketStartTime(SRTSOCKET u) diff --git a/srtcore/core.h b/srtcore/core.h index 64e02ef11..7f8d7cb8d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -186,30 +186,30 @@ class CUDT ~CUDT(); public: //API - static int startup(); - static int cleanup(); + static SRTSTATUS startup(); + static SRTSTATUS cleanup(); static SRTSOCKET socket(); #if ENABLE_BONDING static SRTSOCKET createGroup(SRT_GROUP_TYPE); static SRTSOCKET getGroupOfSocket(SRTSOCKET socket); static int getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize); - static bool isgroup(SRTSOCKET sock) { return (sock & SRTGROUP_MASK) != 0; } + static bool isgroup(SRTSOCKET sock) { return (int32_t(sock) & SRTGROUP_MASK) != 0; } #endif - static int bind(SRTSOCKET u, const sockaddr* name, int namelen); - static int bind(SRTSOCKET u, UDPSOCKET udpsock); - static int listen(SRTSOCKET u, int backlog); + static SRTSTATUS bind(SRTSOCKET u, const sockaddr* name, int namelen); + static SRTSTATUS bind(SRTSOCKET u, UDPSOCKET udpsock); + static SRTSTATUS listen(SRTSOCKET u, int backlog); static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen); static SRTSOCKET accept_bond(const SRTSOCKET listeners [], int lsize, int64_t msTimeOut); - static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); - static int connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn); + static SRTSOCKET connect(SRTSOCKET u, const sockaddr* name, const sockaddr* tname, int namelen); #if ENABLE_BONDING - static int connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); + static SRTSOCKET connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); #endif - static int close(SRTSOCKET u); - static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); - static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); - static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); + static SRTSTATUS close(SRTSOCKET u); + static SRTSTATUS getpeername(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockname(SRTSOCKET u, sockaddr* name, int* namelen); + static SRTSTATUS getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); + static SRTSTATUS setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen); static int send(SRTSOCKET u, const char* buf, int len, int flags); static int recv(SRTSOCKET u, char* buf, int len, int flags); static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = SRT_MSGTTL_INF, bool inorder = false, int64_t srctime = 0); @@ -221,29 +221,29 @@ class CUDT static int select(int nfds, UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); static int selectEx(const std::vector& fds, std::vector* readfds, std::vector* writefds, std::vector* exceptfds, int64_t msTimeOut); static int epoll_create(); - static int epoll_clear_usocks(int eid); - static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); - static int epoll_remove_usock(const int eid, const SRTSOCKET u); - static int epoll_remove_ssock(const int eid, const SYSSOCKET s); - static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); - static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_clear_usocks(int eid); + static SRTSTATUS epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); + static SRTSTATUS epoll_remove_usock(const int eid, const SRTSOCKET u); + static SRTSTATUS epoll_remove_ssock(const int eid, const SYSSOCKET s); + static SRTSTATUS epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL); + static SRTSTATUS epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL); static int epoll_wait(const int eid, std::set* readfds, std::set* writefds, int64_t msTimeOut, std::set* lrfds = NULL, std::set* wrfds = NULL); static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); static int32_t epoll_set(const int eid, int32_t flags); - static int epoll_release(const int eid); + static SRTSTATUS epoll_release(const int eid); static CUDTException& getlasterror(); - static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); + static SRTSTATUS bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false); #if ENABLE_BONDING - static int groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); + static SRTSTATUS groupsockbstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true); #endif static SRT_SOCKSTATUS getsockstate(SRTSOCKET u); static bool setstreamid(SRTSOCKET u, const std::string& sid); static std::string getstreamid(SRTSOCKET u); - static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); + static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes); // returns buffer span in [ms] static int rejectReason(SRTSOCKET s); - static int rejectReason(SRTSOCKET s, int value); + static SRTSTATUS rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); public: // internal API @@ -252,16 +252,15 @@ class CUDT { APIError(const CUDTException&); APIError(CodeMajor, CodeMinor, int = 0); + APIError(int error_code); + // This represents both SRT_ERROR and SRT_INVALID_SOCK. operator int() const { return SRT_ERROR; } }; - static const SRTSOCKET INVALID_SOCK = -1; // Invalid socket descriptor - static const int ERROR = -1; // Socket api error returned value - static const int HS_VERSION_UDT4 = 4; static const int HS_VERSION_SRT1 = 5; @@ -938,8 +937,8 @@ class CUDT // FORWARDER public: - static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); - static int installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); + static SRTSTATUS installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); + static SRTSTATUS installConnectHook(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); private: void installAcceptHook(srt_listen_callback_fn* hook, void* opaq) { diff --git a/srtcore/crypto.cpp b/srtcore/crypto.cpp index 866ec2fa7..d4c22d69d 100644 --- a/srtcore/crypto.cpp +++ b/srtcore/crypto.cpp @@ -695,7 +695,7 @@ void srt::CCryptoControl::close() std::string srt::CCryptoControl::CONID() const { - if (m_SocketID == 0) + if (int32_t(m_SocketID) <= 0) return ""; std::ostringstream os; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 4040a21be..0117a95e2 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -165,7 +165,7 @@ ENOMEM: There was insufficient memory to create the kernel object. return m_iIDSeed; } -int srt::CEPoll::clear_usocks(int eid) +void srt::CEPoll::clear_usocks(int eid) { // This should remove all SRT sockets from given eid. ScopedLock pg (m_EPollLock); @@ -177,8 +177,6 @@ int srt::CEPoll::clear_usocks(int eid) CEPollDesc& d = p->second; d.clearAll(); - - return 0; } @@ -219,7 +217,7 @@ void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction) d.removeSubscription(cleared[i]); } -int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -287,11 +285,9 @@ int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events) #endif p->second.m_sLocals.insert(s); - - return 0; } -int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) +void srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) { ScopedLock pg(m_EPollLock); @@ -317,12 +313,10 @@ int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s) #endif p->second.m_sLocals.erase(s); - - return 0; } // Need this to atomically modify polled events (ex: remove write/keep read) -int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) +void srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events) { ScopedLock pg(m_EPollLock); IF_HEAVY_LOGGING(ostringstream evd); @@ -391,10 +385,9 @@ int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* even HLOGC(ealog.Debug, log << "srt_epoll_update_usock: REMOVED E" << eid << " socket @" << u); d.removeSubscription(u); } - return 0; } -int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) +void srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events) { ScopedLock pg(m_EPollLock); @@ -462,10 +455,9 @@ int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* even // Assuming add is used if not inserted // p->second.m_sLocals.insert(s); - return 0; } -int srt::CEPoll::setflags(const int eid, int32_t flags) +int32_t srt::CEPoll::setflags(const int eid, int32_t flags) { ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); @@ -845,7 +837,7 @@ bool srt::CEPoll::empty(const CEPollDesc& d) const return d.watch_empty(); } -int srt::CEPoll::release(const int eid) +void srt::CEPoll::release(const int eid) { ScopedLock pg(m_EPollLock); @@ -861,8 +853,6 @@ int srt::CEPoll::release(const int eid) #endif m_mPolls.erase(i); - - return 0; } diff --git a/srtcore/epoll.h b/srtcore/epoll.h index 7b0d941c8..d0ccec25d 100644 --- a/srtcore/epoll.h +++ b/srtcore/epoll.h @@ -374,38 +374,29 @@ friend class srt::CRendezvousQueue; /// delete all user sockets (SRT sockets) from an EPoll /// @param [in] eid EPoll ID. - /// @return 0 - int clear_usocks(int eid); + void clear_usocks(int eid); /// add a system socket to an EPoll. /// @param [in] eid EPoll ID. /// @param [in] s system Socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// remove a system socket event from an EPoll; socket will be removed if no events to watch. /// @param [in] eid EPoll ID. /// @param [in] s system socket ID. - /// @return 0 if success, otherwise an error number. - - int remove_ssock(const int eid, const SYSSOCKET& s); + void remove_ssock(const int eid, const SYSSOCKET& s); /// update a UDT socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_usock(const int eid, const SRTSOCKET& u, const int* events); + void update_usock(const int eid, const SRTSOCKET& u, const int* events); /// update a system socket events from an EPoll. /// @param [in] eid EPoll ID. /// @param [in] u UDT socket ID. /// @param [in] events events to watch. - /// @return 0 if success, otherwise an error number. - - int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); + void update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL); /// wait for EPoll events or timeout. /// @param [in] eid EPoll ID. @@ -472,7 +463,7 @@ friend class srt::CRendezvousQueue; /// @param [in] eid EPoll ID. /// @return 0 if success, otherwise an error number. - int release(const int eid); + void release(const int eid); public: // for CUDT to acknowledge IO status @@ -486,7 +477,7 @@ friend class srt::CRendezvousQueue; int update_events(const SRTSOCKET& uid, std::set& eids, int events, bool enable); - int setflags(const int eid, int32_t flags); + int32_t setflags(const int eid, int32_t flags); private: int m_iIDSeed; // seed to generate a new ID diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 541636b44..e7e50cb91 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -1443,7 +1443,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) ; p.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; - p.hdr[SRT_PH_ID] = rcv.id; + p.hdr[SRT_PH_ID] = int32_t(rcv.id); // Header ready, now we rebuild the contents // First, rebuild the length. diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 572e0d746..766eabc26 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -249,8 +249,8 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) : m_Global(CUDT::uglobal()) - , m_GroupID(-1) - , m_PeerGroupID(-1) + , m_GroupID(SRT_INVALID_SOCK) + , m_PeerGroupID(SRT_INVALID_SOCK) , m_type(gtype) , m_listener() , m_iBusy() @@ -913,7 +913,7 @@ void CUDTGroup::close() // removing themselves from the group when closing because they // are unaware of being group members. m_Group.clear(); - m_PeerGroupID = -1; + m_PeerGroupID = SRT_INVALID_SOCK; set epollid; { diff --git a/srtcore/group.h b/srtcore/group.h index 09e072267..99826e333 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -757,7 +757,7 @@ class CUDTGroup { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_GroupID << ":"; + os << "$" << m_GroupID << ":"; return os.str(); #else return ""; diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index f8f03c84d..63854b9ad 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -88,7 +88,7 @@ int srt::CHandShake::store_to(char* buf, size_t& w_size) *p++ = m_iMSS; *p++ = m_iFlightFlagSize; *p++ = int32_t(m_iReqType); - *p++ = m_iID; + *p++ = int32_t(m_iID); *p++ = m_iCookie; for (int i = 0; i < 4; ++ i) *p++ = m_piPeerIP[i]; @@ -111,7 +111,7 @@ int srt::CHandShake::load_from(const char* buf, size_t size) m_iMSS = *p++; m_iFlightFlagSize = *p++; m_iReqType = UDTRequestType(*p++); - m_iID = *p++; + m_iID = SRTSOCKET(*p++); m_iCookie = *p++; for (int i = 0; i < 4; ++ i) m_piPeerIP[i] = *p++; diff --git a/srtcore/handshake.h b/srtcore/handshake.h index 93a351f39..a505abf7a 100644 --- a/srtcore/handshake.h +++ b/srtcore/handshake.h @@ -318,7 +318,7 @@ class CHandShake int32_t m_iMSS; // maximum segment size int32_t m_iFlightFlagSize; // flow control window size UDTRequestType m_iReqType; // handshake stage - int32_t m_iID; // SRT socket ID of HS sender + SRTSOCKET m_iID; // SRT socket ID of HS sender int32_t m_iCookie; // cookie uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index bf5db8c0d..a855552cd 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -600,7 +596,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index e6d2516a9..4f17d4df4 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -349,12 +349,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1b05c4f4e..e7a9ca2bb 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 1bdff1848..7cbe928ae 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -731,10 +731,10 @@ void srt::CHash::init(int size) m_iHashSize = size; } -srt::CUDT* srt::CHash::lookup(int32_t id) +srt::CUDT* srt::CHash::lookup(SRTSOCKET id) { // simple hash function (% hash table size); suitable for socket descriptors - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); while (NULL != b) { @@ -746,21 +746,21 @@ srt::CUDT* srt::CHash::lookup(int32_t id) return NULL; } -void srt::CHash::insert(int32_t id, CUDT* u) +void srt::CHash::insert(SRTSOCKET id, CUDT* u) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* n = new CBucket; n->m_iID = id; n->m_pUDT = u; n->m_pNext = b; - m_pBucket[id % m_iHashSize] = n; + bucketAt(id) = n; } -void srt::CHash::remove(int32_t id) +void srt::CHash::remove(SRTSOCKET id) { - CBucket* b = m_pBucket[id % m_iHashSize]; + CBucket* b = bucketAt(id); CBucket* p = NULL; while (NULL != b) @@ -768,7 +768,7 @@ void srt::CHash::remove(int32_t id) if (id == b->m_iID) { if (NULL == p) - m_pBucket[id % m_iHashSize] = b->m_pNext; + bucketAt(id) = b->m_pNext; else p->m_pNext = b->m_pNext; @@ -831,12 +831,12 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& { ScopedLock vg(m_RIDListLock); - IF_HEAVY_LOGGING(const char* const id_type = w_id ? "THIS ID" : "A NEW CONNECTION"); + IF_HEAVY_LOGGING(const char* const id_type = w_id == SRT_SOCKID_CONNREQ ? "A NEW CONNECTION" : "THIS ID" ); // TODO: optimize search for (list::const_iterator i = m_lRendezvousID.begin(); i != m_lRendezvousID.end(); ++i) { - if (i->m_PeerAddr == addr && ((w_id == 0) || (w_id == i->m_iID))) + if (i->m_PeerAddr == addr && ((w_id == SRT_SOCKID_CONNREQ) || (w_id == i->m_iID))) { // This procedure doesn't exactly respond to the original UDT idea. // As the "rendezvous queue" is used for both handling rendezvous and @@ -855,7 +855,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& // This means: if an incoming ID is 0, then this search should succeed ONLY // IF THE FOUND SOCKET WAS RENDEZVOUS. - if (!w_id && !i->m_pUDT->m_config.bRendezvous) + if (w_id == SRT_SOCKID_CONNREQ && !i->m_pUDT->m_config.bRendezvous) { HLOGC(cnlog.Debug, log << "RID: found id @" << i->m_iID << " while looking for " @@ -874,7 +874,7 @@ srt::CUDT* srt::CRendezvousQueue::retrieve(const sockaddr_any& addr, SRTSOCKET& #if ENABLE_HEAVY_LOGGING std::ostringstream spec; - if (w_id == 0) + if (w_id == SRT_SOCKID_CONNREQ) spec << "A NEW CONNECTION REQUEST"; else spec << " AGENT @" << w_id; @@ -894,7 +894,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : SRT_SOCKID_CONNREQ; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -925,7 +925,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst EReadStatus read_st = rst; EConnectStatus conn_st = cst; - if (cst != CONN_RENDEZVOUS && dest_id != 0) + if (cst != CONN_RENDEZVOUS && dest_id != SRT_SOCKID_CONNREQ) { if (i->id != dest_id) { @@ -1011,7 +1011,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, EConnectStatus cst SRT_ATR_UNUSED, - int iDstSockID, + SRTSOCKET iDstSockID, vector& toRemove, vector& toProcess) { @@ -1151,7 +1151,7 @@ srt::CRcvQueue::~CRcvQueue() delete m_pRendezvousQueue; // remove all queued messages - for (map >::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) + for (qmap_t::iterator i = m_mBuffer.begin(); i != m_mBuffer.end(); ++i) { while (!i->second.empty()) { @@ -1201,7 +1201,7 @@ void* srt::CRcvQueue::worker(void* param) { CRcvQueue* self = (CRcvQueue*)param; sockaddr_any sa(self->getIPversion()); - int32_t id = 0; + SRTSOCKET id = SRT_SOCKID_CONNREQ; #if ENABLE_LOGGING THREAD_STATE_INIT(("SRT:RcvQ:w" + Sprint(m_counter)).c_str()); @@ -1217,7 +1217,7 @@ void* srt::CRcvQueue::worker(void* param) EReadStatus rst = self->worker_RetrieveUnit((id), (unit), (sa)); if (rst == RST_OK) { - if (id < 0) + if (id < 0) // Any negative (illegal range) and SRT_INVALID_SOCKET { // User error on peer. May log something, but generally can only ignore it. // XXX Think maybe about sending some "connection rejection response". @@ -1234,7 +1234,7 @@ void* srt::CRcvQueue::worker(void* param) // Note to rendezvous connection. This can accept: // - ID == 0 - take the first waiting rendezvous socket // - ID > 0 - find the rendezvous socket that has this ID. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { // ID 0 is for connection request, which should be passed to the listening socket or rendezvous sockets cst = self->worker_ProcessConnectionRequest(unit, sa); @@ -1330,7 +1330,7 @@ void* srt::CRcvQueue::worker(void* param) return NULL; } -srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_unit, sockaddr_any& w_addr) +srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(SRTSOCKET& w_id, CUnit*& w_unit, sockaddr_any& w_addr) { #if !USE_BUSY_WAITING // This might be not really necessary, and probably @@ -1380,7 +1380,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); @@ -1428,10 +1428,12 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, } // If there's no listener waiting for the packet, just store it into the queue. - return worker_TryAsyncRend_OrStore(0, unit, addr); // 0 id because the packet came in with that very ID. + // Passing SRT_SOCKID_CONNREQ explicitly because it's a handler for a HS packet + // that came for this very ID. + return worker_TryAsyncRend_OrStore(SRT_SOCKID_CONNREQ, unit, addr); } -srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& addr) { CUDT* u = m_pHash->lookup(id); if (!u) @@ -1483,7 +1485,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessAddressedPacket(int32_t id, CU // This function then tries to manage the packet as a rendezvous connection // request in ASYNC mode; when this is not applicable, it stores the packet // in the "receiving queue" so that it will be picked up in the "main" thread. -srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& addr) +srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(SRTSOCKET id, CUnit* unit, const sockaddr_any& addr) { // This 'retrieve' requires that 'id' be either one of those // stored in the rendezvous queue (see CRcvQueue::registerConnector) @@ -1504,7 +1506,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_TryAsyncRend_OrStore(int32_t id, CUni // not belonging to the connection and not registered as rendezvous) as "possible // attack" and ignore it. This also should better protect the rendezvous socket // against a rogue connector. - if (id == 0) + if (id == SRT_SOCKID_CONNREQ) { HLOGC(cnlog.Debug, log << CONID() << "AsyncOrRND: no sockets expect connection from " << addr.str() @@ -1630,11 +1632,11 @@ void srt::CRcvQueue::stopWorker() m_WorkerThread.join(); } -int srt::CRcvQueue::recvfrom(int32_t id, CPacket& w_packet) +int srt::CRcvQueue::recvfrom(SRTSOCKET id, CPacket& w_packet) { CUniqueSync buffercond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { @@ -1719,7 +1721,7 @@ void srt::CRcvQueue::removeConnector(const SRTSOCKET& id) ScopedLock bufferlock(m_BufferLock); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i != m_mBuffer.end()) { HLOGC(cnlog.Debug, @@ -1759,11 +1761,11 @@ srt::CUDT* srt::CRcvQueue::getNewEntry() return u; } -void srt::CRcvQueue::storePkt(int32_t id, CPacket* pkt) +void srt::CRcvQueue::storePkt(SRTSOCKET id, CPacket* pkt) { CUniqueSync passcond(m_BufferLock, m_BufferCond); - map >::iterator i = m_mBuffer.find(id); + qmap_t::iterator i = m_mBuffer.find(id); if (i == m_mBuffer.end()) { diff --git a/srtcore/queue.h b/srtcore/queue.h index 208b23058..8b25b2d0c 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -279,23 +279,23 @@ class CHash /// @param [in] id socket ID /// @return Pointer to a UDT instance, or NULL if not found. - CUDT* lookup(int32_t id); + CUDT* lookup(SRTSOCKET id); /// Insert an entry to the hash table. /// @param [in] id socket ID /// @param [in] u pointer to the UDT instance - void insert(int32_t id, CUDT* u); + void insert(SRTSOCKET id, CUDT* u); /// Remove an entry from the hash table. /// @param [in] id socket ID - void remove(int32_t id); + void remove(SRTSOCKET id); private: struct CBucket { - int32_t m_iID; // Socket ID + SRTSOCKET m_iID; // Socket ID CUDT* m_pUDT; // Socket instance CBucket* m_pNext; // next bucket @@ -303,6 +303,11 @@ class CHash int m_iHashSize; // size of hash table + CBucket*& bucketAt(SRTSOCKET id) + { + return m_pBucket[int32_t(id) % m_iHashSize]; + } + private: CHash(const CHash&); CHash& operator=(const CHash&); @@ -374,7 +379,7 @@ class CRendezvousQueue /// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request. bool qualifyToHandle(EReadStatus rst, EConnectStatus cst, - int iDstSockID, + SRTSOCKET iDstSockID, std::vector& toRemove, std::vector& toProcess); @@ -505,7 +510,7 @@ class CRcvQueue /// @param [in] id Socket ID /// @param [out] packet received packet /// @return Data size of the packet - int recvfrom(int32_t id, CPacket& to_packet); + int recvfrom(SRTSOCKET id, CPacket& to_packet); void stopWorker(); @@ -517,10 +522,10 @@ class CRcvQueue static void* worker(void* param); sync::CThread m_WorkerThread; // Subroutines of worker - EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa); + EReadStatus worker_RetrieveUnit(SRTSOCKET& id, CUnit*& unit, sockaddr_any& sa); EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa); - EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_TryAsyncRend_OrStore(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); + EConnectStatus worker_ProcessAddressedPacket(SRTSOCKET id, CUnit* unit, const sockaddr_any& sa); private: CUnitQueue* m_pUnitQueue; // The received packet queue @@ -551,7 +556,7 @@ class CRcvQueue bool ifNewEntry(); CUDT* getNewEntry(); - void storePkt(int32_t id, CPacket* pkt); + void storePkt(SRTSOCKET id, CPacket* pkt); private: sync::Mutex m_LSLock; @@ -561,9 +566,10 @@ class CRcvQueue std::vector m_vNewEntry; // newly added entries, to be inserted sync::Mutex m_IDLock; - std::map > m_mBuffer; // temporary buffer for rendezvous connection request - sync::Mutex m_BufferLock; - sync::Condition m_BufferCond; + typedef std::map > qmap_t; + qmap_t m_mBuffer; // temporary buffer for rendezvous connection request + sync::Mutex m_BufferLock; + sync::Condition m_BufferCond; private: CRcvQueue(const CRcvQueue&); diff --git a/srtcore/srt.h b/srtcore/srt.h index 4bdbdf4b6..5b49e5e36 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -139,6 +139,7 @@ extern "C" { #endif typedef int32_t SRTSOCKET; +typedef int SRTSTATUS; // The most significant bit 31 (sign bit actually) is left unused, // so that all people who check the value for < 0 instead of -1 @@ -148,6 +149,12 @@ typedef int32_t SRTSOCKET; // socket or a socket group. static const int32_t SRTGROUP_MASK = (1 << 30); +#ifdef __cplusplus +namespace srt { +inline bool isgroup(SRTSOCKET sid) { return (int32_t(sid) & SRTGROUP_MASK) != 0; } +} +#endif + #ifdef _WIN32 typedef SOCKET SYSSOCKET; #else @@ -741,11 +748,13 @@ inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2) typedef struct CBytePerfMon SRT_TRACEBSTATS; static const SRTSOCKET SRT_INVALID_SOCK = -1; -static const int SRT_ERROR = -1; +static const SRTSOCKET SRT_SOCKID_CONNREQ = 0; +static const SRTSTATUS SRT_ERROR = -1; +static const SRTSTATUS SRT_STATUS_OK = 0; // library initialization -SRT_API int srt_startup(void); -SRT_API int srt_cleanup(void); +SRT_API SRTSTATUS srt_startup(void); +SRT_API SRTSTATUS srt_cleanup(void); // // Socket operations @@ -756,33 +765,33 @@ SRT_API int srt_cleanup(void); SRT_ATR_DEPRECATED_PX SRT_API SRTSOCKET srt_socket(int, int, int) SRT_ATR_DEPRECATED; SRT_API SRTSOCKET srt_create_socket(void); -SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); +SRT_API SRTSTATUS srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSTATUS srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock); // Old name of srt_bind_acquire(), please don't use // Planned deprecation removal: rel1.6.0 -SRT_ATR_DEPRECATED_PX static inline int srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; -static inline int srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } -SRT_API int srt_listen (SRTSOCKET u, int backlog); +SRT_ATR_DEPRECATED_PX static inline SRTSTATUS srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED; +static inline SRTSTATUS srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); } +SRT_API SRTSTATUS srt_listen (SRTSOCKET u, int backlog); SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen); SRT_API SRTSOCKET srt_accept_bond (const SRTSOCKET listeners[], int lsize, int64_t msTimeOut); typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid); -SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); typedef void srt_connect_callback_fn (void* opaq, SRTSOCKET ns, int errorcode, const struct sockaddr* peeraddr, int token); -SRT_API int srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); -SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); -SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); -SRT_API int srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, +SRT_API SRTSTATUS srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRT_API SRTSOCKET srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen); +SRT_API SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRT_API SRTSOCKET srt_connect_bind (SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); -SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRT_API SRTSTATUS srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); -SRT_API int srt_close (SRTSOCKET u); -SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); -SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); -SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); -SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); -SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API SRTSTATUS srt_close (SRTSOCKET u); +SRT_API SRTSTATUS srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); +SRT_API SRTSTATUS srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); +SRT_API SRTSTATUS srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen); +SRT_API SRTSTATUS srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); +SRT_API SRTSTATUS srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; @@ -834,18 +843,18 @@ SRT_API extern const SRT_MSGCTRL srt_msgctrl_default; // // Sending functions // -SRT_API int srt_send (SRTSOCKET u, const char* buf, int len); -SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/); -SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); +SRT_API int srt_send (SRTSOCKET u, const char* buf, int len); +SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/); +SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl); // // Receiving functions // -SRT_API int srt_recv (SRTSOCKET u, char* buf, int len); +SRT_API int srt_recv (SRTSOCKET u, char* buf, int len); // srt_recvmsg is actually an alias to srt_recv, it stays under the old name for compat reasons. -SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len); -SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl); +SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len); +SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl); // Special send/receive functions for files only. @@ -857,29 +866,29 @@ SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int // last error detection SRT_API const char* srt_getlasterror_str(void); -SRT_API int srt_getlasterror(int* errno_loc); +SRT_API int srt_getlasterror(int* errno_loc); SRT_API const char* srt_strerror(int code, int errnoval); SRT_API void srt_clearlasterror(void); // Performance tracking // Performance monitor with Byte counters for better bitrate estimation. -SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRT_API SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRT_API SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); // Socket Status (for problem tracking) SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u); -SRT_API int srt_epoll_create(void); -SRT_API int srt_epoll_clear_usocks(int eid); -SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u); -SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s); -SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API int srt_epoll_create(void); +SRT_API SRTSTATUS srt_epoll_clear_usocks(int eid); +SRT_API SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRT_API SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRT_API SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRT_API SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRT_API SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); -SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut, +SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut, SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum); typedef struct SRT_EPOLL_EVENT_STR { @@ -887,13 +896,13 @@ typedef struct SRT_EPOLL_EVENT_STR int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR #ifdef __cplusplus SRT_EPOLL_EVENT_STR(SRTSOCKET s, int ev): fd(s), events(ev) {} - SRT_EPOLL_EVENT_STR(): fd(-1), events(0) {} // NOTE: allows singular values, no init. + SRT_EPOLL_EVENT_STR(): fd(SRT_INVALID_SOCK), events(0) {} // NOTE: allows singular values, no init. #endif } SRT_EPOLL_EVENT; -SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); +SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); SRT_API int32_t srt_epoll_set(int eid, int32_t flags); -SRT_API int srt_epoll_release(int eid); +SRT_API SRTSTATUS srt_epoll_release(int eid); // Logging control @@ -908,10 +917,10 @@ SRT_API void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler); SRT_API void srt_setlogflags(int flags); -SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); +SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); -SRT_API int srt_getrejectreason(SRTSOCKET sock); -SRT_API int srt_setrejectreason(SRTSOCKET sock, int value); +SRT_API int srt_getrejectreason(SRTSOCKET sock); +SRT_API SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); // The srt_rejectreason_msg[] array is deprecated (as unsafe). // Planned removal: v1.6.0. SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg []; @@ -984,14 +993,14 @@ typedef struct SRT_GroupMemberConfig_ SRT_API SRTSOCKET srt_create_group(SRT_GROUP_TYPE); SRT_API SRTSOCKET srt_groupof(SRTSOCKET socket); -SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); +SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen); SRT_API SRT_SOCKOPT_CONFIG* srt_create_config(void); SRT_API void srt_delete_config(SRT_SOCKOPT_CONFIG* config /*nullable*/); -SRT_API int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); +SRT_API SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len); SRT_API SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, const struct sockaddr* adr, int namelen); -SRT_API int srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); +SRT_API SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize); #ifdef __cplusplus } diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 2b5f3d72f..430205b37 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -29,8 +29,8 @@ using namespace srt; extern "C" { -int srt_startup() { return CUDT::startup(); } -int srt_cleanup() { return CUDT::cleanup(); } +SRTSTATUS srt_startup() { return CUDT::startup(); } +SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } // Socket creation. SRTSOCKET srt_socket(int , int , int ) { return CUDT::socket(); } @@ -50,18 +50,18 @@ SRT_SOCKOPT_CONFIG* srt_create_config() return new SRT_SocketOptionObject; } -int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len) { if (!config) - return -1; + return SRT_ERROR; if (!config->add(option, contents, len)) - return -1; + return SRT_ERROR; - return 0; + return SRT_STATUS_OK; } -int srt_connect_group(SRTSOCKET group, +SRTSOCKET srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize) { return CUDT::connectLinks(group, name, arraysize); @@ -71,11 +71,11 @@ int srt_connect_group(SRTSOCKET group, SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return SRT_INVALID_SOCK; } SRTSOCKET srt_groupof(SRTSOCKET) { return SRT_INVALID_SOCK; } -int srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } -int srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } -int srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSTATUS srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } #endif @@ -87,7 +87,7 @@ SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struc #else data.errorcode = SRT_EINVOP; #endif - data.id = -1; + data.id = SRT_INVALID_SOCK; data.token = -1; data.weight = 0; data.config = NULL; @@ -109,21 +109,21 @@ void srt_delete_config(SRT_SOCKOPT_CONFIG* in) } // Binding and connection management -int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } -int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); } +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); } +SRTSTATUS srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); } SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); } SRTSOCKET srt_accept_bond(const SRTSOCKET lsns[], int lsize, int64_t msTimeOut) { return CUDT::accept_bond(lsns, lsize, msTimeOut); } -int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } -int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } -int srt_connect_bind(SRTSOCKET u, +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); } +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); } +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int target_len) { return CUDT::connect(u, source, target, target_len); } -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { bool yes = 1; @@ -135,14 +135,20 @@ int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_nam || local_name->sa_family != remote_name->sa_family) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); - const int st = srt_bind(u, local_name, local_namelen); - if (st != 0) + const SRTSTATUS st = srt_bind(u, local_name, local_namelen); + if (st != SRT_STATUS_OK) return st; - return srt_connect(u, remote_name, remote_namelen); + // Note: srt_connect may potentially return a socket value if it is used + // to connect a group. But rendezvous is not supported for groups. + const SRTSOCKET sst = srt_connect(u, remote_name, remote_namelen); + if (sst == SRT_INVALID_SOCK) + return SRT_ERROR; + + return SRT_STATUS_OK; } -int srt_close(SRTSOCKET u) +SRTSTATUS srt_close(SRTSOCKET u) { SRT_SOCKSTATUS st = srt_getsockstate(u); @@ -151,22 +157,22 @@ int srt_close(SRTSOCKET u) (st == SRTS_CLOSING) ) { // It's closed already. Do nothing. - return 0; + return SRT_STATUS_OK; } return CUDT::close(u); } -int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } -int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } -int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); } +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen) { return CUDT::getsockopt(u, level, optname, optval, optlen); } -int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen) { return CUDT::setsockopt(u, level, optname, optval, optlen); } -int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) +SRTSTATUS srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) { return CUDT::getsockopt(u, 0, opt, optval, optlen); } -int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } @@ -261,21 +267,21 @@ void srt_clearlasterror() UDT::getlasterror().clear(); } -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); } +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); } SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::getsockstate(u)); } // event mechanism int srt_epoll_create() { return CUDT::epoll_create(); } -int srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } +SRTSTATUS srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); } // You can use either SRT_EPOLL_* flags or EPOLL* flags from , both are the same. IN/OUT/ERR only. // events == NULL accepted, in which case all flags are set. -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); } -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -289,15 +295,15 @@ int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events) return CUDT::epoll_add_ssock(eid, s, &flag); } -int srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } -int srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); } +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); } -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_update_usock(eid, u, events); } -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events) { int flag = 0; @@ -338,7 +344,7 @@ int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTim // Pass -1 to not change anything (but still get the current flag value). int32_t srt_epoll_set(int eid, int32_t flags) { return CUDT::epoll_set(eid, flags); } -int srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } +SRTSTATUS srt_epoll_release(int eid) { return CUDT::epoll_release(eid); } void srt_setloglevel(int ll) { @@ -380,12 +386,12 @@ int srt_getrejectreason(SRTSOCKET sock) return CUDT::rejectReason(sock); } -int srt_setrejectreason(SRTSOCKET sock, int value) +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value) { return CUDT::rejectReason(sock, value); } -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) { if (!hook) return CUDT::APIError(MJ_NOTSUP, MN_INVAL); @@ -393,7 +399,7 @@ int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq) return CUDT::installAcceptHook(lsn, hook, opaq); } -int srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) +SRTSTATUS srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq) { if (!hook) return CUDT::APIError(MJ_NOTSUP, MN_INVAL); diff --git a/srtcore/tsbpd_time.cpp b/srtcore/tsbpd_time.cpp index 046c90b74..23c67badd 100644 --- a/srtcore/tsbpd_time.cpp +++ b/srtcore/tsbpd_time.cpp @@ -162,7 +162,7 @@ void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wra m_bTsbPdWrapCheck = wrap; // Timebase passed here comes is calculated as: - // Tnow - hspkt.m_iTimeStamp + // Tnow - hspkt.timestamp() // where hspkt is the packet with SRT_CMD_HSREQ message. // // This function is called in the HSREQ reception handler only. diff --git a/srtcore/udt.h b/srtcore/udt.h index 22f6be870..d0e1afac1 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -166,7 +166,7 @@ typedef std::set UDSET; SRT_API extern const SRTSOCKET INVALID_SOCK; #undef ERROR -SRT_API extern const int ERROR; +SRT_API extern const SRTSTATUS ERROR; SRT_API int startup(); SRT_API int cleanup(); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 31e05b205..a9f3bdba1 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1089,6 +1089,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index ecc4a4947..a9dba46ba 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -236,7 +236,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -257,7 +257,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -267,7 +267,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -282,7 +282,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 15356c8f2..db0db317a 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,21 +54,26 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + } + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index 75386bcb9..edaac17f1 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -86,9 +86,9 @@ namespace srt const int inorder = 1; const int kflg = m_crypt.getSndCryptoFlags(); - pkt.m_iSeqNo = seqno; - pkt.m_iMsgNo = msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg);; - pkt.m_iTimeStamp = 356; + pkt.set_seqno(seqno); + pkt.set_msgflags(msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg)); + pkt.set_timestamp(356); std::iota(pkt.data(), pkt.data() + pld_size, '0'); pkt.setLength(pld_size); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index f9dfc393c..5d4d423da 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -222,7 +222,7 @@ TEST(CEPoll, HandleEpollEvent) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -238,7 +238,7 @@ TEST(CEPoll, HandleEpollEvent) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -248,7 +248,7 @@ TEST(CEPoll, HandleEpollEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -399,7 +399,7 @@ TEST(CEPoll, HandleEpollEvent2) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR | SRT_EPOLL_ET; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -420,7 +420,7 @@ TEST(CEPoll, HandleEpollEvent2) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -430,7 +430,7 @@ TEST(CEPoll, HandleEpollEvent2) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -461,7 +461,7 @@ TEST(CEPoll, HandleEpollNoEvent) ASSERT_GE(epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); SRT_EPOLL_EVENT fds[1024]; @@ -472,7 +472,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -482,7 +482,7 @@ TEST(CEPoll, HandleEpollNoEvent) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { @@ -515,7 +515,7 @@ TEST(CEPoll, ThreadedUpdate) this_thread::sleep_for(chrono::seconds(1)); // Make sure that uwait will be called as first cerr << "ADDING sockets to eid\n"; const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); + epoll.update_usock(epoll_id, client_sock, &epoll_out); set epoll_ids = { epoll_id }; @@ -539,7 +539,7 @@ TEST(CEPoll, ThreadedUpdate) try { int no_events = 0; - EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0); + epoll.update_usock(epoll_id, client_sock, &no_events); } catch (CUDTException &ex) { @@ -549,7 +549,7 @@ TEST(CEPoll, ThreadedUpdate) try { - EXPECT_EQ(epoll.release(epoll_id), 0); + epoll.release(epoll_id); } catch (CUDTException &ex) { diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..91d269221 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -792,7 +792,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -869,7 +869,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -887,7 +887,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b07f92ae8..f28aef811 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -471,10 +471,10 @@ void SrtCommon::InitParameters(string host, string path, map par) void SrtCommon::PrepareListener(string host, int port, int backlog) { m_bindsock = srt_create_socket(); - if (m_bindsock == SRT_ERROR) + if (m_bindsock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_bindsock); + SRTSTATUS stat = ConfigurePre(m_bindsock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -529,7 +529,7 @@ void SrtCommon::AcceptNewClient() int len = 2; SRTSOCKET ready[2]; - while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == -1) + while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == SRT_ERROR) { if (::transmit_int_state) Error("srt_epoll_wait for srt_accept: interrupt"); @@ -552,7 +552,7 @@ void SrtCommon::AcceptNewClient() } #if ENABLE_BONDING - if (m_sock & SRTGROUP_MASK) + if (srt::isgroup(m_sock)) { m_listener_group = true; if (m_group_config != "") @@ -565,7 +565,7 @@ void SrtCommon::AcceptNewClient() #ifndef SRT_OLD_APP_READER - if (srt_epoll != -1) + if (srt_epoll != SRT_ERROR) { Verb() << "(Group: erasing epoll " << srt_epoll << ") " << VerbNoEOL; srt_epoll_release(srt_epoll); @@ -588,14 +588,14 @@ void SrtCommon::AcceptNewClient() { sockaddr_any peeraddr(AF_INET6); string peer = ""; - if (-1 != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) + if (SRT_ERROR != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len))) { peer = peeraddr.str(); } sockaddr_any agentaddr(AF_INET6); string agent = ""; - if (-1 != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) + if (SRT_ERROR != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len))) { agent = agentaddr.str(); } @@ -606,7 +606,7 @@ void SrtCommon::AcceptNewClient() // ConfigurePre is done on bindsock, so any possible Pre flags // are DERIVED by sock. ConfigurePost is done exclusively on sock. - int stat = ConfigurePost(m_sock); + SRTSTATUS stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) Error("ConfigurePost"); } @@ -731,7 +731,7 @@ void SrtCommon::Init(string host, int port, string path, map par, if (!m_blocking_mode) { // Don't add new epoll if already created as a part - // of group management: if (srt_epoll == -1)... + // of group management: if (srt_epoll == SRT_ERROR)... if (m_mode == "caller") dir = (dir | SRT_EPOLL_UPDATE); @@ -744,7 +744,7 @@ void SrtCommon::Init(string host, int port, string path, map par, int SrtCommon::AddPoller(SRTSOCKET socket, int modes) { int pollid = srt_epoll_create(); - if (pollid == -1) + if (pollid == SRT_ERROR) throw std::runtime_error("Can't create epoll in nonblocking mode"); Verb() << "EPOLL: creating eid=" << pollid << " and adding @" << socket << " in " << DirectionName(SRT_EPOLL_OPT(modes)) << " mode"; @@ -752,15 +752,15 @@ int SrtCommon::AddPoller(SRTSOCKET socket, int modes) return pollid; } -int SrtCommon::ConfigurePost(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePost(SRTSOCKET sock) { bool yes = m_blocking_mode; - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; if (m_direction & SRT_EPOLL_OUT) { Verb() << "Setting SND blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -771,7 +771,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) if (m_timeout) result = srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout); - if (result == -1) + if (result == SRT_ERROR) { #ifdef PLEASE_LOG extern srt_logging::Logger applog; @@ -785,7 +785,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) { Verb() << "Setting RCV blocking mode: " << boolalpha << yes << " timeout=" << m_timeout; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; if (m_timeout) @@ -795,7 +795,7 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) int timeout = 1000; result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout); } - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -816,18 +816,18 @@ int SrtCommon::ConfigurePost(SRTSOCKET sock) } } - return 0; + return SRT_STATUS_OK; } -int SrtCommon::ConfigurePre(SRTSOCKET sock) +SRTSTATUS SrtCommon::ConfigurePre(SRTSOCKET sock) { - int result = 0; + SRTSTATUS result = SRT_STATUS_OK; int no = 0; if (!m_tsbpdmode) { result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no); - if (result == -1) + if (result == SRT_ERROR) return result; } @@ -835,7 +835,7 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) // This is for asynchronous connect. int maybe = m_blocking_mode; result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &maybe, sizeof maybe); - if (result == -1) + if (result == SRT_ERROR) return result; // host is only checked for emptiness and depending on that the connection mode is selected. @@ -859,13 +859,13 @@ int SrtCommon::ConfigurePre(SRTSOCKET sock) return SRT_ERROR; } - return 0; + return SRT_STATUS_OK; } void SrtCommon::SetupAdapter(const string& host, int port) { auto lsa = CreateAddr(host, port); - int stat = srt_bind(m_sock, lsa.get(), sizeof lsa); + SRTSTATUS stat = srt_bind(m_sock, lsa.get(), sizeof lsa); if (stat == SRT_ERROR) Error("srt_bind"); } @@ -885,10 +885,10 @@ void SrtCommon::OpenClient(string host, int port) void SrtCommon::PrepareClient() { m_sock = srt_create_socket(); - if (m_sock == SRT_ERROR) + if (m_sock == SRT_INVALID_SOCK) Error("srt_create_socket"); - int stat = ConfigurePre(m_sock); + SRTSTATUS stat = ConfigurePre(m_sock); if (stat == SRT_ERROR) Error("ConfigurePre"); @@ -959,12 +959,12 @@ void SrtCommon::OpenGroupClient() } m_sock = srt_create_group(type); - if (m_sock == -1) + if (m_sock == SRT_ERROR) Error("srt_create_group"); srt_connect_callback(m_sock, &TransmitGroupSocketConnect, this); - int stat = -1; + int stat = SRT_ERROR; if (m_group_config != "") { Verb() << "Ignoring setting group config: '" << m_group_config; @@ -972,7 +972,7 @@ void SrtCommon::OpenGroupClient() stat = ConfigurePre(m_sock); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) Error("ConfigurePre"); if (!m_blocking_mode) @@ -1097,7 +1097,7 @@ void SrtCommon::OpenGroupClient() // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[i].id != SRT_INVALID_SOCK && targets[i].errorcode == SRT_SUCCESS) { m_group_nodes[i].socket = targets[i].id; } @@ -1107,14 +1107,14 @@ void SrtCommon::OpenGroupClient() // should be added to epoll. size_t size = m_group_data.size(); stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); stat = srt_group_data(m_sock, m_group_data.data(), &size); } - if (stat == -1) + if (stat == SRT_ERROR) { Error("srt_group_data"); } @@ -1123,7 +1123,7 @@ void SrtCommon::OpenGroupClient() for (size_t i = 0; i < m_group_nodes.size(); ++i) { SRTSOCKET insock = m_group_nodes[i].socket; - if (insock == -1) + if (insock == SRT_INVALID_SOCK) { Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; continue; @@ -1152,7 +1152,7 @@ void SrtCommon::OpenGroupClient() ready_conn, &len1, -1, // Wait infinitely NULL, NULL, - NULL, NULL) != -1) + NULL, NULL) != SRT_ERROR) { Verb() << "[C]" << VerbNoEOL; for (int i = 0; i < len1; ++i) @@ -1211,7 +1211,7 @@ void SrtCommon::OpenGroupClient() } stat = ConfigurePost(m_sock); - if (stat == -1) + if (stat == SRT_ERROR) { // This kind of error must reject the whole operation. // Usually you'll get this error on the first socket, @@ -1287,13 +1287,13 @@ void SrtCommon::ConnectClient(string host, int port) srt_connect_callback(m_sock, &TransmitConnectCallback, 0); } - int stat = -1; + SRTSTATUS stat = SRT_ERROR; for (;;) { ::transmit_throw_on_interrupt = true; - stat = srt_connect(m_sock, sa.get(), sizeof sa); + SRTSOCKET stats = srt_connect(m_sock, sa.get(), sizeof sa); ::transmit_throw_on_interrupt = false; - if (stat == SRT_ERROR) + if (stats == SRT_INVALID_SOCK) { int reason = srt_getrejectreason(m_sock); #if PLEASE_LOG @@ -1325,7 +1325,7 @@ void SrtCommon::ConnectClient(string host, int port) // Socket readiness for connection is checked by polling on WRITE allowed sockets. int lenc = 2, lene = 2; SRTSOCKET ready_connect[2], ready_error[2]; - if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != -1) + if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != SRT_ERROR) { // We should have just one socket, so check whatever socket // is in the transmit_error_storage. @@ -1502,7 +1502,7 @@ void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpda int result = d.result; SRT_MEMBERSTATUS mstatus = d.memberstate; - if (result != -1 && status == SRTS_CONNECTED) + if (result != SRT_ERROR && status == SRTS_CONNECTED) { // Short report with the state. Verb() << "G@" << id << "<" << MemberStatusStr(mstatus) << "> " << VerbNoEOL; @@ -1648,7 +1648,7 @@ bytevector SrtSource::GroupRead(size_t chunk) size_t size = m_group_data.size(); int stat = srt_group_data(m_sock, m_group_data.data(), &size); - if (stat == -1 && size > m_group_data.size()) + if (stat == SRT_ERROR && size > m_group_data.size()) { // Just too small buffer. Resize and continue. m_group_data.resize(size); @@ -1660,7 +1660,7 @@ bytevector SrtSource::GroupRead(size_t chunk) m_group_data.resize(size); } - if (stat == -1) // Also after the above fix + if (stat == SRT_ERROR) // Also after the above fix { Error(UDT::getlasterror(), "FAILURE when reading group data"); } @@ -1696,7 +1696,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // Check first the ahead packets if you have any to deliver. - if (m_group_seqno != -1 && !m_group_positions.empty()) + if (m_group_seqno != SRT_SEQNO_NONE && !m_group_positions.empty()) { bytevector ahead_packet; @@ -1951,7 +1951,7 @@ bytevector SrtSource::GroupRead(size_t chunk) } // NOTE: checks against m_group_seqno and decisions based on it - // must NOT be done if m_group_seqno is -1, which means that we + // must NOT be done if m_group_seqno is NONE, which means that we // are about to deliver the very first packet and we take its // sequence number as a good deal. @@ -1961,11 +1961,11 @@ bytevector SrtSource::GroupRead(size_t chunk) // - check ordering. // The second one must be done always, but failed discrepancy // check should exclude the socket from any further checks. - // That's why the common check for m_group_seqno != -1 can't + // That's why the common check for m_group_seqno != NONE can't // embrace everything below. // We need to first qualify the sequence, just for a case - if (m_group_seqno != -1 && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) + if (m_group_seqno != SRT_SEQNO_NONE && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH) { // This error should be returned if the link turns out // to be the only one, or set to the group data. @@ -1993,7 +1993,7 @@ bytevector SrtSource::GroupRead(size_t chunk) p->sequence = mctrl.pktseq; } - if (m_group_seqno != -1) + if (m_group_seqno != SRT_SEQNO_NONE) { // Now we can safely check it. int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_group_seqno); @@ -2082,7 +2082,7 @@ bytevector SrtSource::GroupRead(size_t chunk) // Update it now and don't do anything else with the sockets. // Sanity check - if (next_seq == -1) + if (next_seq == SRT_SEQNO_NONE) { Error("IPE: next_seq not set after output extracted!"); } @@ -2306,7 +2306,7 @@ MediaPacket SrtSource::Read(size_t chunk) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != SRT_ERROR) { Verb() << "... epoll reported ready " << len << " sockets"; // If the event was SRT_EPOLL_UPDATE, report it, and still wait. @@ -2409,7 +2409,7 @@ SrtTarget::SrtTarget(std::string host, int port, std::string path, const std::ma int SrtTarget::ConfigurePre(SRTSOCKET sock) { int result = SrtCommon::ConfigurePre(sock); - if (result == -1) + if (result == SRT_ERROR) return result; int yes = 1; @@ -2418,7 +2418,7 @@ int SrtTarget::ConfigurePre(SRTSOCKET sock) // In HSv4 this setting is obligatory; otherwise the SRT handshake // extension will not be done at all. result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes); - if (result == -1) + if (result == SRT_ERROR) return result; return 0; @@ -2437,7 +2437,7 @@ void SrtTarget::Write(const MediaPacket& data) int len = 2; SRT_EPOLL_EVENT sready[2]; len = srt_epoll_uwait(srt_epoll, sready, len, -1); - if (len != -1) + if (len != SRT_ERROR) { bool any_write_ready = false; for (int i = 0; i < len; ++i) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..f7c49ddf6 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -142,7 +142,7 @@ class SrtCommon void Acquire(SRTSOCKET s) { m_sock = s; - if (s & SRTGROUP_MASK) + if (srt::isgroup(s)) m_listener_group = true; } @@ -153,8 +153,8 @@ class SrtCommon void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); void Init(string host, int port, string path, map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); - virtual int ConfigurePost(SRTSOCKET sock); - virtual int ConfigurePre(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePost(SRTSOCKET sock); + virtual SRTSTATUS ConfigurePre(SRTSOCKET sock); void OpenClient(string host, int port); #if ENABLE_BONDING @@ -226,7 +226,7 @@ class SrtTarget: public virtual Target, public virtual SrtCommon SrtTarget(std::string host, int port, std::string path, const std::map& par); SrtTarget() {} - int ConfigurePre(SRTSOCKET sock) override; + SRTSTATUS ConfigurePre(SRTSOCKET sock) override; void Write(const MediaPacket& data) override; bool IsOpen() override { return IsUsable(); } bool Broken() override { return IsBroken(); } @@ -249,7 +249,7 @@ class SrtRelay: public Relay, public SrtSource, public SrtTarget SrtRelay(std::string host, int port, std::string path, const std::map& par); SrtRelay() {} - int ConfigurePre(SRTSOCKET sock) override + SRTSTATUS ConfigurePre(SRTSOCKET sock) override { // This overrides the change introduced in SrtTarget, // which sets the SRTO_SENDER flag. For a bidirectional transmission From 6ad10417a35b77b35a5b5d4f96fdefdb488f3372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 22 Dec 2022 18:29:43 +0100 Subject: [PATCH 051/517] [MAINT] Fixed example buildbreaks. Added examples for message mode transmission --- .github/workflows/cxx11-ubuntu.yaml | 2 +- CMakeLists.txt | 4 + examples/recvlive.cpp | 10 -- examples/recvmsg.cpp | 215 ++++++++++++++++++++++++++++ examples/sendmsg.cpp | 210 +++++++++++++++++++++++++++ examples/test-c-client.c | 2 +- examples/test-c-server.c | 2 +- 7 files changed, 432 insertions(+), 13 deletions(-) create mode 100644 examples/recvmsg.cpp create mode 100644 examples/sendmsg.cpp diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 61a3f21d6..751b3fda1 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -16,7 +16,7 @@ jobs: - name: configure run: | mkdir _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON - name: build run: cd _build && cmake --build ./ - name: test diff --git a/CMakeLists.txt b/CMakeLists.txt index 7586e79e5..b62c67a95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1339,6 +1339,10 @@ if (ENABLE_EXAMPLES) srt_add_example(recvfile.cpp) + srt_add_example(sendmsg.cpp) + + srt_add_example(recvmsg.cpp) + srt_add_example(test-c-client.c) srt_add_example(example-client-nonblock.c) diff --git a/examples/recvlive.cpp b/examples/recvlive.cpp index b2a026d03..81c2b1ef7 100644 --- a/examples/recvlive.cpp +++ b/examples/recvlive.cpp @@ -53,16 +53,6 @@ int main(int argc, char* argv[]) return 0; } - // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, - // although there's also lots of other options to be set, for which there's a convenience option, - // SRTO_TRANSTYPE. - // SRT_TRANSTYPE tt = SRTT_LIVE; - // if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_TRANSTYPE, &tt, sizeof tt)) - // { - // cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; - // return 0; - // } - bool no = false; if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_RCVSYN, &no, sizeof no)) { diff --git a/examples/recvmsg.cpp b/examples/recvmsg.cpp new file mode 100644 index 000000000..008eb3928 --- /dev/null +++ b/examples/recvmsg.cpp @@ -0,0 +1,215 @@ +#ifndef WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +string CreateFilename(string fmt, int ord) +{ + ostringstream os; + + size_t pos = fmt.find('%'); + if (pos == string::npos) + os << fmt << ord << ".out"; + else + { + os << fmt.substr(0, pos) << ord << fmt.substr(pos+1); + } + return os.str(); +} + +int main(int argc, char* argv[]) +{ + string service("9000"); + if (argc > 1) + service = argv[1]; + + if (service == "--help") + { + cout << "usage: recvmsg [server_port] [filepattern]" << endl; + return 0; + } + + addrinfo hints; + addrinfo* res; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res)) + { + cout << "illegal port number or port is busy.\n" << endl; + return 1; + } + + string outfileform; + if (argc > 2) + { + outfileform = argv[2]; + } + + // use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + SRTSOCKET sfd = srt_create_socket(); + if (SRT_INVALID_SOCK == sfd) + { + cout << "srt_socket: " << srt_getlasterror_str() << endl; + return 1; + } + + int file_mode = SRTT_FILE; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_TRANSTYPE, &file_mode, sizeof file_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + bool message_mode = true; + if (SRT_ERROR == srt_setsockflag(sfd, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode)) + { + cout << "srt_setsockopt: " << srt_getlasterror_str() << endl; + return 1; + } + + if (SRT_ERROR == srt_bind(sfd, res->ai_addr, res->ai_addrlen)) + { + cout << "srt_bind: " << srt_getlasterror_str() << endl; + return 0; + } + + freeaddrinfo(res); + + cout << "server is ready at port: " << service << endl; + + if (SRT_ERROR == srt_listen(sfd, 10)) + { + cout << "srt_listen: " << srt_getlasterror_str() << endl; + return 1; + } + + char data[4096]; + + srt::sockaddr_any remote; + + int afd = srt_accept(sfd, remote.get(), &remote.len); + + if (afd == SRT_INVALID_SOCK) + { + cout << "srt_accept: " << srt_getlasterror_str() << endl; + return 1; + } + + cout << "Connection from " << remote.str() << " established\n"; + + bool save_to_files = true; + + if (outfileform != "") + save_to_files = true; + + int ordinal = 1; + + // the event loop + while (true) + { + SRT_SOCKSTATUS status = srt_getsockstate(afd); + if ((status == SRTS_BROKEN) || + (status == SRTS_NONEXIST) || + (status == SRTS_CLOSED)) + { + cout << "source disconnected. status=" << status << endl; + srt_close(afd); + break; + } + + int ret = srt_recvmsg(afd, data, sizeof(data)); + if (ret == SRT_ERROR) + { + cout << "srt_recvmsg: " << srt_getlasterror_str() << endl; + break; + } + if (ret == 0) + { + cout << "EOT\n"; + break; + } + + if (ret < 5) + { + cout << "WRONG MESSAGE SYNTAX\n"; + break; + } + + if (save_to_files) + { + string fname = CreateFilename(outfileform, ordinal++); + ofstream ofile(fname); + + if (!ofile.good()) + { + cout << "ERROR: can't create file: " << fname << " - skipping message\n"; + continue; + } + + ofile.write(data, ret); + ofile.close(); + cout << "Written " << ret << " bytes of message to " << fname << endl; + } + else + { + union + { + char chars[4]; + int32_t intval; + } first4; + + copy(data, data + 4, first4.chars); + + cout << "[" << ret << "B " << ntohl(first4.intval) << "] "; + for (int i = 4; i < ret; ++i) + cout << ShowChar(data[i]); + cout << endl; + } + } + + srt_close(afd); + srt_close(sfd); + + // use this function to release the UDT library + srt_cleanup(); + + return 0; +} + +// Local Variables: +// c-file-style: "ellemtel" +// c-basic-offset: 3 +// compile-command: "g++ -Wall -O2 -std=c++11 -I.. -I../srtcore -o recvlive recvlive.cpp -L.. -lsrt -lpthread -L/usr/local/opt/openssl/lib -lssl -lcrypto" +// End: diff --git a/examples/sendmsg.cpp b/examples/sendmsg.cpp new file mode 100644 index 000000000..53a0f66a5 --- /dev/null +++ b/examples/sendmsg.cpp @@ -0,0 +1,210 @@ +#ifndef _WIN32 + #include + #include +#else + #include + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +string ShowChar(char in) +{ + if (in >= 32 && in < 127) + return string(1, in); + + ostringstream os; + os << "<" << hex << uppercase << int(in) << ">"; + return os.str(); +} + +int main(int argc, char* argv[]) +{ + if ((argc != 4) || (0 == atoi(argv[2]))) + { + cout << "usage: sendmsg server_ip server_port source_filename" << endl; + return -1; + } + + // Use this function to initialize the UDT library + srt_startup(); + + srt_setloglevel(srt_logging::LogLevel::debug); + + struct addrinfo hints, *peer; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + SRTSOCKET fhandle = srt_create_socket(); + // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option, + // although there's also lots of other options to be set, for which there's a convenience option, + // SRTO_TRANSTYPE. + SRT_TRANSTYPE tt = SRTT_FILE; + srt_setsockopt(fhandle, 0, SRTO_TRANSTYPE, &tt, sizeof tt); + + bool message_mode = true; + srt_setsockopt(fhandle, 0, SRTO_MESSAGEAPI, &message_mode, sizeof message_mode); + + if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer)) + { + cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl; + return -1; + } + + // Connect to the server, implicit bind. + if (SRT_ERROR == srt_connect(fhandle, peer->ai_addr, peer->ai_addrlen)) + { + int rej = srt_getrejectreason(fhandle); + cout << "connect: " << srt_getlasterror_str() << ":" << srt_rejectreason_str(rej) << endl; + return -1; + } + + freeaddrinfo(peer); + + string source_fname = argv[3]; + + bool use_filelist = false; + if (source_fname[0] == '+') + { + use_filelist = true; + source_fname = source_fname.substr(1); + } + + istream* pin; + ifstream fin; + if (source_fname == "-") + { + pin = &cin; + } + else + { + fin.open(source_fname.c_str()); + pin = &fin; + } + + // The syntax is: + // + // 1. If the first character is +, it's followed by TTL in milliseconds. + // 2. Otherwise the first number is the ID, followed by a space, to be filled in first 4 bytes. + // 3. Rest of the characters, up to the end of line, should be put into a solid block and sent at once. + + int status = 0; + + int ordinal = 1; + int lpos = 0; + + for (;;) + { + string line; + int32_t id = 0; + int ttl = -1; + + getline(*pin, (line)); + if (pin->eof()) + break; + + // Interpret + if (line.size() < 2) + continue; + + if (use_filelist) + { + // The line should contain [+TTL] FILENAME + char fname[1024] = ""; + if (line[0] == '+') + { + int nparsed = sscanf(line.c_str(), "+%d %n%1000s", &ttl, &lpos, fname); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + line = fname; + } + + ifstream ifile (line); + + id = ordinal; + ++ordinal; + + if (!ifile.good()) + { + cout << "ERROR: file '" << line << "' cannot be read, skipping\n"; + continue; + } + + line = string(istreambuf_iterator(ifile), istreambuf_iterator()); + } + else + { + int lpos = 0; + + int nparsed = 0; + if (line[0] == '+') + { + nparsed = sscanf(line.c_str(), "+%d %d %n%*s", &ttl, &id, &lpos); + if (nparsed != 2) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + else + { + nparsed = sscanf(line.c_str(), "%d %n%*s", &id, &lpos); + if (nparsed != 1) + { + cout << "ERROR: syntax error in input (" << nparsed << " parsed pos=" << lpos << ")\n"; + status = SRT_ERROR; + break; + } + } + } + + union + { + char chars[4]; + int32_t intval; + } first4; + first4.intval = htonl(id); + + vector input; + copy(first4.chars, first4.chars+4, back_inserter(input)); + copy(line.begin() + lpos, line.end(), back_inserter(input)); + + // CHECK CODE + // cout << "WILL SEND: TTL=" << ttl << " "; + // transform(input.begin(), input.end(), + // ostream_iterator(cout), ShowChar); + // cout << endl; + + int nsnd = srt_sendmsg(fhandle, &input[0], input.size(), ttl, false); + if (nsnd == SRT_ERROR) + { + cout << "SRT ERROR: " << srt_getlasterror_str() << endl; + status = SRT_ERROR; + break; + } + } + + srt_close(fhandle); + + // Signal to the SRT library to clean up all allocated sockets and resources. + srt_cleanup(); + + return status; +} diff --git a/examples/test-c-client.c b/examples/test-c-client.c index a4d99745c..71f6d30e4 100644 --- a/examples/test-c-client.c +++ b/examples/test-c-client.c @@ -59,7 +59,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; diff --git a/examples/test-c-server.c b/examples/test-c-server.c index f81f297eb..f7faf6fba 100644 --- a/examples/test-c-server.c +++ b/examples/test-c-server.c @@ -54,7 +54,7 @@ int main(int argc, char** argv) } printf("srt setsockflag\n"); - if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes) + if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes)) { fprintf(stderr, "srt_setsockflag: %s\n", srt_getlasterror_str()); return 1; From 29e8c1944fb02c2f7ef5a95684132a26613ab2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 11:25:40 +0100 Subject: [PATCH 052/517] Fixed wrong assertion for drop snd buf sequence check --- srtcore/buffer_snd.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 7e55ecad4..2f29a2c6c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -494,7 +494,13 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); - SRT_ASSERT(w_drop.seqno[Drop::END] == p->m_iSeqNo); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[Drop::END] == CSeqNo::decseq(p->m_iSeqNo)); return READ_DROP; } From ce98b8ac53d69cd2b7df8e5f8b8724c1df988594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 12:50:42 +0100 Subject: [PATCH 053/517] [MAINT] Refax for CSndBuffer::readData to make the call form clearer --- srtcore/buffer_snd.cpp | 50 +++++++++++++++++++++++++++--------------- srtcore/buffer_snd.h | 25 ++++++++++++++++++--- srtcore/core.cpp | 25 ++++++++++----------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index e945b166c..d63f97519 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -421,9 +421,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, Drop& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -438,19 +439,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. // If so, then inform the caller that it should first take care of the whole // message (all blocks with that message id). Shift the m_pCurrBlock pointer // to the position past the last of them. Then return -1 and set the - // msgno_bitset return reference to the message id that should be dropped as + // msgno bitset packet field to the message id that should be dropped as // a whole. // After taking care of that, the caller should immediately call this function again, @@ -462,11 +467,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -476,18 +481,27 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[Drop::BEGIN] = w_packet.m_iSeqNo; + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[Drop::END] == CSeqNo::decseq(p->m_iSeqNo)); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5dce96ae0..d0dcdd453 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct Drop + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. + /// @param [in,out] packet the packet to read. /// @param [out] origintime origin time stamp of the message /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, Drop& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index d6b4d7389..1822e8213 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9071,28 +9071,25 @@ int srt::CUDT::packLostData(CPacket& w_packet) } } - int msglen; + CSndBuffer::Drop buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[CSndBuffer::Drop::BEGIN], buffer_drop.seqno[CSndBuffer::Drop::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[CSndBuffer::Drop::BEGIN] << " - " + << buffer_drop.seqno[CSndBuffer::Drop::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[CSndBuffer::Drop::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[CSndBuffer::Drop::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored From 333eaf2eab8979c6321b9940a19cbef159898f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 23 Dec 2022 13:35:40 +0100 Subject: [PATCH 054/517] [MAINT] Removed reference fields from CPacket pinned to the SRT header --- srtcore/buffer_snd.cpp | 14 +-- srtcore/channel.cpp | 4 +- srtcore/core.cpp | 223 ++++++++++++++++++----------------- srtcore/packet.cpp | 6 +- srtcore/packet.h | 11 +- srtcore/packetfilter.cpp | 2 +- srtcore/queue.cpp | 4 +- srtcore/utilities.h | 1 + srtcore/window.h | 8 +- test/test_buffer_rcv.cpp | 19 +-- test/test_fec_rebuilding.cpp | 6 +- 11 files changed, 155 insertions(+), 143 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index d63f97519..2f29a2c6c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -320,7 +320,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -346,7 +346,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -448,7 +448,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // This is rexmit request, so the packet should have the sequence number // already set when it was once sent uniquely. - SRT_ASSERT(p->m_iSeqNo == w_packet.m_iSeqNo); + SRT_ASSERT(p->m_iSeqNo == w_packet.seqno()); // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale. @@ -492,8 +492,8 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // to simply take the sequence number from the block. But this is a new // feature and should be only used after refax for the sender buffer to // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. - w_drop.seqno[Drop::BEGIN] = w_packet.m_iSeqNo; - w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.m_iSeqNo, msglen - 1); + w_drop.seqno[Drop::BEGIN] = w_packet.seqno(); + w_drop.seqno[Drop::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); // Note the rules: here `p` is pointing to the first block AFTER the // message to be dropped, so the end sequence should be one behind @@ -514,7 +514,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -522,7 +522,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 30fd98778..4f752f290 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -613,8 +613,8 @@ void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet) const { HLOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp << " " << packet.Info()); + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << " " << packet.Info()); #ifdef SRT_TEST_FAKE_LOSS diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1822e8213..ef3734518 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -1963,7 +1963,7 @@ bool srt::CUDT::processSrtMsg(const CPacket *ctrlpkt) uint32_t *srtdata = (uint32_t *)ctrlpkt->m_pcData; size_t len = ctrlpkt->getLength(); int etype = ctrlpkt->getExtendedType(); - uint32_t ts = ctrlpkt->m_iTimeStamp; + uint32_t ts = ctrlpkt->timestamp(); int res = SRT_CMD_NONE; @@ -2494,7 +2494,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2521,7 +2521,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3525,7 +3525,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(0); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3540,7 +3540,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3605,7 +3605,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) << " > 250 ms). size=" << reqpkt.getLength()); if (m_config.bRendezvous) - reqpkt.m_iID = m_ConnRes.m_iID; + reqpkt.set_id(m_ConnRes.m_iID); #if ENABLE_HEAVY_LOGGING { @@ -3823,17 +3823,17 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, // This should have got the original value returned from // processConnectResponse through processAsyncConnectResponse. - CPacket request; - request.setControl(UMSG_HANDSHAKE); - request.allocate(m_iMaxSRTPayloadSize); + CPacket reqpkt; + reqpkt.setControl(UMSG_HANDSHAKE); + reqpkt.allocate(m_iMaxSRTPayloadSize); const steady_clock::time_point now = steady_clock::now(); - setPacketTS(request, now); + setPacketTS(reqpkt, now); HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: REQ-TIME: HIGH. Should prevent too quick responses."); m_tsLastReqTime = now; // ID = 0, connection request - request.m_iID = !m_config.bRendezvous ? 0 : m_ConnRes.m_iID; + reqpkt.set_id(!m_config.bRendezvous ? 0 : m_ConnRes.m_iID); bool status = true; @@ -3844,7 +3844,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (cst == CONN_RENDEZVOUS) { HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: passing to processRendezvous"); - cst = processRendezvous(pResponse, serv_addr, rst, (request)); + cst = processRendezvous(pResponse, serv_addr, rst, (reqpkt)); if (cst == CONN_ACCEPT) { HLOGC(cnlog.Debug, @@ -3876,8 +3876,8 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, { // (this procedure will be also run for HSv4 rendezvous) HLOGC(cnlog.Debug, - log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << request.getLength()); - if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (request), (m_ConnReq))) + log << CONID() << "processAsyncConnectRequest: serializing HS: buffer size=" << reqpkt.getLength()); + if (!createSrtHandshake(SRT_CMD_HSREQ, SRT_CMD_KMREQ, 0, 0, (reqpkt), (m_ConnReq))) { // All 'false' returns from here are IPE-type, mostly "invalid argument" plus "all keys expired". LOGC(cnlog.Error, @@ -3889,7 +3889,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: sending HS reqtype=" << RequestTypeStr(m_ConnReq.m_iReqType) - << " to socket " << request.m_iID << " size=" << request.getLength()); + << " to socket " << reqpkt.id() << " size=" << reqpkt.getLength()); } } @@ -3899,16 +3899,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request); + m_pSndQueue->sendto(serv_addr, reqpkt); return status; } @@ -5679,15 +5679,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& size_t size = m_iMaxSRTPayloadSize; // Allocate the maximum possible memory for an SRT payload. // This is a maximum you can send once. - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(size); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(size); // This will serialize the handshake according to its current form. HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: creating CONCLUSION response (HSv5: with HSRSP/KMRSP) buffer size=" << size); - if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (w_hs))) + if (!createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (w_hs))) { LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: error creating handshake response"); throw CUDTException(MJ_SETUP, MN_REJECTED, 0); @@ -5698,10 +5698,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show()); } #endif @@ -5711,7 +5711,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } // This function is required to be called when a caller receives an INDUCTION @@ -5917,7 +5917,7 @@ void srt::CUDT::checkSndKMRefresh() void srt::CUDT::addressAndSend(CPacket& w_pkt) { - w_pkt.m_iID = m_PeerID; + w_pkt.set_id(m_PeerID); setPacketTS(w_pkt, steady_clock::now()); // NOTE: w_pkt isn't modified in this call, @@ -7625,8 +7625,8 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; - nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); + ctrlpkt.set_id(m_PeerID); + nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7640,7 +7640,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7661,7 +7661,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); enterCS(m_StatsLock); @@ -7691,7 +7691,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); m_tsLastWarningTime = steady_clock::now(); @@ -7700,14 +7700,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7716,21 +7716,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); break; @@ -7817,7 +7817,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8024,7 +8024,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8716,18 +8716,18 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. if (createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, - (response), (initdata))) + (rsppkt), (initdata))) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -8849,7 +8849,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) HLOGC(inlog.Debug, log << CONID() << "incoming UMSG:" << ctrlpkt.getType() << " (" - << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.m_iID); + << MessageTypeStr(ctrlpkt.getType(), ctrlpkt.getExtendedType()) << ") socket=%" << ctrlpkt.id()); switch (ctrlpkt.getType()) { @@ -9024,37 +9024,44 @@ int srt::CUDT::packLostData(CPacket& w_packet) const steady_clock::time_point time_now = steady_clock::now(); const steady_clock::time_point time_nak = time_now - microseconds_from(m_iSRTT - 4 * m_iRTTVar); - while ((w_packet.m_iSeqNo = m_pSndLossList->popLostSeq()) >= 0) + for (;;) { + w_packet.set_seqno(m_pSndLossList->popLostSeq()); + if (w_packet.seqno() == SRT_SEQNO_NONE) + break; + // XXX See the note above the m_iSndLastDataAck declaration in core.h // This is the place where the important sequence numbers for // sender buffer are actually managed by this field here. - const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.m_iSeqNo); + const int offset = CSeqNo::seqoff(m_iSndLastDataAck, w_packet.seqno()); if (offset < 0) { // XXX Likely that this will never be executed because if the upper // sequence is not in the sender buffer, then most likely the loss // was completely ignored. LOGC(qrlog.Error, - log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(m_iSeqNo " - << w_packet.m_iSeqNo << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset - << ". Continue"); + log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be // considered, and some LOSSREPORT flood prevention), send the drop request // to the peer. int32_t seqpair[2] = { - w_packet.m_iSeqNo, + w_packet.seqno(), CSeqNo::decseq(m_iSndLastDataAck) }; - w_packet.m_iMsgNo = 0; // Message number is not known, setting all 32 bits to 0. HLOGC(qrlog.Debug, - log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: msg=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" + log << CONID() << "PEER reported LOSS not from the sending buffer - requesting DROP: #" + << MSGNO_SEQ::unwrap(w_packet.msgflags()) << " SEQ:" << seqpair[0] << " - " << seqpair[1] << "(" << (-offset) << " packets)"); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + // See interpretation in processCtrlDropReq(). We don't know the message number, + // so we request that the drop be exclusively sequence number based. + int32_t msgno = SRT_MSGNO_CONTROL; + + sendCtrl(UMSG_DROPREQ, &msgno, seqpair, sizeof(seqpair)); continue; } @@ -9064,7 +9071,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) if (tsLastRexmit >= time_nak) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " - << w_packet.m_iSeqNo << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) + << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; @@ -9114,7 +9121,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // So, set here the rexmit flag if the peer understands it. if (m_bPeerRexmitFlag) { - w_packet.m_iMsgNo |= PACKET_SND_REXMIT; + w_packet.set_msgflags(w_packet.msgflags() | PACKET_SND_REXMIT); } setDataPacketTS(w_packet, tsOrigin); @@ -9215,7 +9222,7 @@ void srt::CUDT::setPacketTS(CPacket& p, const time_point& ts) enterCS(m_StatsLock); const time_point tsStart = m_stats.tsStartTime; leaveCS(m_StatsLock); - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) @@ -9227,14 +9234,14 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) if (!m_bPeerTsbPd) { // If TSBPD is disabled, use the current time as the source (timestamp using the sending time). - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); return; } // TODO: Might be better for performance to ensure this condition is always false, and just use SRT_ASSERT here. if (ts < tsStart) { - p.m_iTimeStamp = makeTS(steady_clock::now(), tsStart); + p.set_timestamp(makeTS(steady_clock::now(), tsStart)); LOGC(qslog.Warn, log << CONID() << "setPacketTS: reference time=" << FormatTime(ts) << " is in the past towards start time=" << FormatTime(tsStart) @@ -9243,7 +9250,7 @@ void srt::CUDT::setDataPacketTS(CPacket& p, const time_point& ts) } // Use the provided source time for the timestamp. - p.m_iTimeStamp = makeTS(ts, tsStart); + p.set_timestamp(makeTS(ts, tsStart)); } bool srt::CUDT::isRetransmissionAllowed(const time_point& tnow SRT_ATR_UNUSED) @@ -9340,14 +9347,14 @@ std::pair srt::CUDT::packData(CPacket& w_packet) new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9357,7 +9364,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9376,7 +9383,7 @@ std::pair srt::CUDT::packData(CPacket& w_packet) // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9462,7 +9469,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(m_iSndCurrSeqNo, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9476,7 +9483,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9484,7 +9491,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packData: Fixing EXTRACTION sequence " << m_iSndCurrSeqNo - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9493,7 +9500,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = m_iSndCurrSeqNo; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9504,17 +9511,17 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // packet are not present in the buffer (preadte the send buffer). // Override extraction sequence with scheduling sequence. - m_iSndCurrSeqNo = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); ScopedLock ackguard(m_RecvAckLock); - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << m_iSndCurrSeqNo << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9526,15 +9533,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packData: Applying EXTRACTION sequence " << m_iSndCurrSeqNo - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(m_iSndCurrSeqNo, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = m_iSndCurrSeqNo; + w_packet.set_seqno(m_iSndCurrSeqNo); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9554,7 +9561,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) } #if SRT_DEBUG_TRACE_SND - g_snd_logger.state.iPktSeqno = w_packet.m_iSeqNo; + g_snd_logger.state.iPktSeqno = w_packet.seqno(); g_snd_logger.state.isRetransmitted = w_packet.getRexmitFlag(); g_snd_logger.trace(); #endif @@ -9725,7 +9732,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bool adding_successful = true; - const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.m_iSeqNo); + const int32_t bufidx = CSeqNo::seqoff(bufseq, rpkt.seqno()); IF_HEAVY_LOGGING(const char *exc_type = "EXPECTED"); @@ -9750,7 +9757,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // which never contains losses, so discarding this packet does not // discard a loss coverage, even if this were past ACK. - if (bufidx < 0 || CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvLastAck) < 0) + if (bufidx < 0 || CSeqNo::seqcmp(rpkt.seqno(), m_iRcvLastAck) < 0) { time_point pts = getPktTsbPdTime(NULL, rpkt); @@ -9763,7 +9770,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& m_stats.rcvr.recvdBelated.count(rpkt.getLength()); leaveCS(m_StatsLock); HLOGC(qrlog.Debug, - log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo << " bufidx=" << bufidx << " (BELATED/" + log << CONID() << "RECEIVED: %" << rpkt.seqno() << " bufidx=" << bufidx << " (BELATED/" << s_rexmitstat_str[pktrexmitflag] << ") with ACK %" << m_iRcvLastAck << " FLAGS: " << rpkt.MessageFlagStr()); continue; @@ -9785,7 +9792,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& LOGC(qrlog.Error, log << CONID() << "SEQUENCE DISCREPANCY. BREAKING CONNECTION." - " %" << rpkt.m_iSeqNo + " %" << rpkt.seqno() << " buffer=(%" << bufseq << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) @@ -9796,7 +9803,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& } else { - LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.m_iSeqNo + LOGC(qrlog.Warn, log << CONID() << "No room to store incoming packet seqno " << rpkt.seqno() << ", insert offset " << bufidx << ". " << m_pRcvBuffer->strFullnessState(m_iRcvLastAck, steady_clock::now()) ); @@ -9887,7 +9894,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Empty buffer info in case of groupwise receiver. // There's no way to obtain this information here. - LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.m_iSeqNo + LOGC(qrlog.Debug, log << CONID() << "RECEIVED: %" << rpkt.seqno() << bufinfo.str() << " RSL=" << expectspec.str() << " SN=" << s_rexmitstat_str[pktrexmitflag] @@ -9901,12 +9908,12 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& { HLOGC(qrlog.Debug, log << CONID() - << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.m_iSeqNo)); + << "CONTIGUITY CHECK: sequence distance: " << CSeqNo::seqoff(m_iRcvCurrSeqNo, rpkt.seqno())); - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. + if (CSeqNo::seqcmp(rpkt.seqno(), CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) // Loss detection. { int32_t seqlo = CSeqNo::incseq(m_iRcvCurrSeqNo); - int32_t seqhi = CSeqNo::decseq(rpkt.m_iSeqNo); + int32_t seqhi = CSeqNo::decseq(rpkt.seqno()); w_srt_loss_seqs.push_back(make_pair(seqlo, seqhi)); HLOGC(qrlog.Debug, log << "pkt/LOSS DETECTED: %" << seqlo << " - %" << seqhi); } @@ -9914,9 +9921,9 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& // Update the current largest sequence number that has been received. // Or it is a retransmitted packet, remove it from receiver loss list. - if (CSeqNo::seqcmp(rpkt.m_iSeqNo, m_iRcvCurrSeqNo) > 0) + if (CSeqNo::seqcmp(rpkt.seqno(), m_iRcvCurrSeqNo) > 0) { - m_iRcvCurrSeqNo = rpkt.m_iSeqNo; // Latest possible received + m_iRcvCurrSeqNo = rpkt.seqno(); // Latest possible received } else { @@ -9964,7 +9971,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Search the sequence in the loss record. rexmit_reason = " by "; ScopedLock lock(m_RcvLossLock); - if (!m_pRcvLossList->find(packet.m_iSeqNo, packet.m_iSeqNo)) + if (!m_pRcvLossList->find(packet.seqno(), packet.seqno())) rexmit_reason += "BLIND"; else rexmit_reason += "NAKREPORT"; @@ -10008,7 +10015,7 @@ int srt::CUDT::processData(CUnit* in_unit) // Conditions and any extra data required for the packet // this function will extract and test as needed. - const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0; + const bool unordered = CSeqNo::seqcmp(packet.seqno(), m_iRcvCurrSeqNo) <= 0; // Retransmitted and unordered packets do not provide expected measurement. // We expect the 16th and 17th packet to be sent regularly, @@ -10034,7 +10041,7 @@ int srt::CUDT::processData(CUnit* in_unit) // supply the missing packet(s), and the loss will no longer be visible for the code that follows. if (packet.getMsgSeq(m_bPeerRexmitFlag) != SRT_MSGNO_CONTROL) // disregard filter-control packets, their seq may mean nothing { - const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.m_iSeqNo); + const int diff = CSeqNo::seqoff(m_iRcvCurrPhySeqNo, packet.seqno()); // Difference between these two sequence numbers is expected to be: // 0 - duplicated last packet (theory only) // 1 - subsequent packet (alright) @@ -10050,13 +10057,13 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " - << CSeqNo::decseq(packet.m_iSeqNo) << "]"); + << CSeqNo::decseq(packet.seqno()) << "]"); } if (diff > 0) { // Record if it was further than latest - m_iRcvCurrPhySeqNo = packet.m_iSeqNo; + m_iRcvCurrPhySeqNo = packet.seqno(); } } @@ -10122,7 +10129,7 @@ int srt::CUDT::processData(CUnit* in_unit) // offset from RcvLastAck in RcvBuffer must remain valid between seqoff() and addData() UniqueLock recvbuf_acklock(m_RcvBufferLock); // Needed for possibly check for needsQuickACK. - const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.m_iSeqNo, m_pRcvBuffer->getStartSeqNo()) < 0); + const bool incoming_belated = (CSeqNo::seqcmp(in_unit->m_Packet.seqno(), m_pRcvBuffer->getStartSeqNo()) < 0); const int res = handleSocketPacketReception(incoming, (new_inserted), @@ -10407,7 +10414,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) void srt::CUDT::unlose(const CPacket &packet) { ScopedLock lg(m_RcvLossLock); - int32_t sequence = packet.m_iSeqNo; + int32_t sequence = packet.seqno(); m_pRcvLossList->remove(sequence); // Rest of this code concerns only the "belated lossreport" feature. @@ -10427,7 +10434,7 @@ void srt::CUDT::unlose(const CPacket &packet) { HLOGC(qrlog.Debug, log << "received out-of-band packet %" << sequence); - const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.m_iSeqNo)); + const int seqdiff = abs(CSeqNo::seqcmp(m_iRcvCurrSeqNo, packet.seqno())); enterCS(m_StatsLock); m_stats.traceReorderDistance = max(seqdiff, m_stats.traceReorderDistance); leaveCS(m_StatsLock); @@ -10642,7 +10649,7 @@ int32_t srt::CUDT::bake(const sockaddr_any& addr, int32_t current_cookie, int co int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) { // XXX ASSUMPTIONS: - // [[using assert(packet.m_iID == 0)]] + // [[using assert(packet.id() == 0)]] HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: received a connection request"); @@ -10723,7 +10730,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) // array need not be aligned to int32_t - changed to union in a hope that using int32_t // inside a union will enforce whole union to be aligned to int32_t. hs.m_iCookie = cookie_val; - packet.m_iID = hs.m_iID; + packet.set_id(hs.m_iID); // Ok, now's the time. The listener sets here the version 5 handshake, // even though the request was 4. This is because the old client would @@ -10791,7 +10798,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ... correct (ORIGINAL) cookie. Proceeding."); } - int32_t id = hs.m_iID; + SRTSOCKET id = hs.m_iID; // HANDSHAKE: The old client sees the version that does not match HS_VERSION_UDT4 (5). // In this case it will respond with URQ_ERROR_REJECT. Rest of the data are the same @@ -10839,8 +10846,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet); } @@ -10951,7 +10958,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp); } else @@ -10962,7 +10969,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet); diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index bf5db8c0d..a855552cd 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -179,10 +179,6 @@ CPacket::CPacket() : m_nHeader() // Silences GCC 12 warning "used uninitialized". , m_extra_pad() , m_data_owned(false) - , m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])) - , m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])) - , m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])) - , m_iID((int32_t&)(m_nHeader[SRT_PH_ID])) , m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef())) { m_nHeader.clear(); @@ -600,7 +596,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index e6d2516a9..4f17d4df4 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -349,12 +349,15 @@ class CPacket CPacket(const CPacket&); public: - int32_t& m_iSeqNo; // alias: sequence number - int32_t& m_iMsgNo; // alias: message number - int32_t& m_iTimeStamp; // alias: timestamp - int32_t& m_iID; // alias: destination SRT socket ID char*& m_pcData; // alias: payload (data packet) / control information fields (control packet) + SRTU_PROPERTY_RO(SRTSOCKET, id, SRTSOCKET(m_nHeader[SRT_PH_ID])); + SRTU_PROPERTY_WO_ARG(SRTSOCKET, id, m_nHeader[SRT_PH_ID] = int32_t(arg)); + + SRTU_PROPERTY_RW(int32_t, seqno, m_nHeader[SRT_PH_SEQNO]); + SRTU_PROPERTY_RW(int32_t, msgflags, m_nHeader[SRT_PH_MSGNO]); + SRTU_PROPERTY_RW(int32_t, timestamp, m_nHeader[SRT_PH_TIMESTAMP]); + // Experimental: sometimes these references don't work! char* getData(); char* release(); diff --git a/srtcore/packetfilter.cpp b/srtcore/packetfilter.cpp index 1b05c4f4e..e7a9ca2bb 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 1bdff1848..bb6920310 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -894,7 +894,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : 0; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -1380,7 +1380,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 31e05b205..a9f3bdba1 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -1089,6 +1089,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index ecc4a4947..a9dba46ba 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -236,7 +236,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -257,7 +257,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -267,7 +267,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -282,7 +282,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 15356c8f2..db0db317a 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,21 +54,26 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + { + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + } + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..91d269221 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -792,7 +792,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -869,7 +869,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -887,7 +887,7 @@ TEST_F(TestFECRebuilding, Rebuild) // Set artificially the SN_REXMIT flag in the skipped source packet // because the rebuilt packet shall have REXMIT flag set. - skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true); + skipped.set_msgflags(skipped.msgflags() | MSGNO_REXMIT::wrap(true)); // Compare the header EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]); From 124ec2b6f54534758e688c10ddc032107a1c9997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 13:21:02 +0100 Subject: [PATCH 055/517] [BUG] Fixed default reject reason for a listener callback --- srtcore/core.cpp | 4 ++++ test/test_listen_callback.cpp | 29 ++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 92c53e988..a065fa3c3 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -62,6 +62,7 @@ modified by #include #include #include "srt.h" +#include "access_control.h" // Required for SRT_REJX_FALLBACK #include "queue.h" #include "api.h" #include "core.h" @@ -11592,6 +11593,8 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak acore->m_HSGroupType = gt; #endif + // Set the default value + acore->m_RejectReason = SRT_REJX_FALLBACK; try { int result = CALLBACK_CALL(m_cbAcceptHook, acore->m_SocketID, hs.m_iVersion, peer, target); @@ -11604,6 +11607,7 @@ bool srt::CUDT::runAcceptHook(CUDT *acore, const sockaddr* peer, const CHandShak return false; } + acore->m_RejectReason = SRT_REJ_UNKNOWN; return true; } diff --git a/test/test_listen_callback.cpp b/test/test_listen_callback.cpp index 4e08715de..a3bdb3003 100644 --- a/test/test_listen_callback.cpp +++ b/test/test_listen_callback.cpp @@ -9,6 +9,7 @@ #endif #include "srt.h" +#include "access_control.h" #include "utilities.h" srt_listen_callback_fn SrtTestListenCallback; @@ -98,8 +99,8 @@ class ListenerCallback { if (results[0].events == SRT_EPOLL_IN) { - int acp = srt_accept(server_sock, NULL, NULL); - if (acp == SRT_ERROR) + SRTSOCKET acp = srt_accept(server_sock, NULL, NULL); + if (acp == SRT_INVALID_SOCK) { std::cout << "[T] Accept failed, so exitting\n"; break; @@ -140,7 +141,7 @@ class ListenerCallback }; -int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion, const struct sockaddr* peeraddr, const char* streamid) +int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid) { using namespace std; @@ -192,6 +193,7 @@ int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion if (!found) { + srt_setrejectreason(ns, SRT_REJX_UNAUTHORIZED); cerr << "TEST: USER NOT FOUND, returning false.\n"; return -1; } @@ -246,6 +248,8 @@ TEST_F(ListenerCallback, SecureSuccess) // EXPECTED RESULT: connected successfully EXPECT_NE(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJ_UNKNOWN); } #if SRT_ENABLE_ENCRYPTION @@ -259,6 +263,8 @@ TEST_F(ListenerCallback, FauxPass) // EXPECTED RESULT: connection rejected EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJ_BADSECRET); } #endif @@ -274,7 +280,24 @@ TEST_F(ListenerCallback, FauxUser) // EXPECTED RESULT: connection rejected EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJX_FALLBACK); } +TEST_F(ListenerCallback, FauxSyntax) +{ + string username_spec = "#!::r=mystream,t=publish"; // No 'u' key specified + string password = "thelocalmanager"; + + ASSERT_NE(srt_setsockflag(client_sock, SRTO_STREAMID, username_spec.c_str(), username_spec.size()), -1); +#if SRT_ENABLE_ENCRYPTION + ASSERT_NE(srt_setsockflag(client_sock, SRTO_PASSPHRASE, password.c_str(), password.size()), -1); +#endif + + // EXPECTED RESULT: connection rejected + EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR); + + EXPECT_EQ(srt_getrejectreason(client_sock), SRT_REJX_UNAUTHORIZED); +} From f14c349cbf2ec8cc980504b7a7c467820650a232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 20:54:58 +0100 Subject: [PATCH 056/517] Added extended reject reason string API --- srtcore/access_control.h | 2 +- srtcore/core.cpp | 6 +++- srtcore/srt.h | 1 + srtcore/srt_c_api.cpp | 62 ++++++++++++++++++++++++++++++++++++++++ testing/testmedia.cpp | 21 +++++++++++--- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/srtcore/access_control.h b/srtcore/access_control.h index 611e1dad8..18357aeee 100644 --- a/srtcore/access_control.h +++ b/srtcore/access_control.h @@ -66,7 +66,7 @@ written by #define SRT_REJX_GW 1502 // The server acts as a gateway and the target endpoint rejected the connection. #define SRT_REJX_DOWN 1503 // The service has been temporarily taken over by a stub reporting this error. The real service can be down for maintenance or crashed. // CODE NOT IN USE 504: unused: timeout not supported -#define SRT_REJX_VERSION 1505 // SRT version not supported. This might be either unsupported backward compatibility, or an upper value of a version. +#define SRT_REJX_VERSION 1505 // Version not supported - either SRT or the application itself. This might be either unsupported backward compatibility, or an upper value of a version. // CODE NOT IN USE 506: unused: negotiation and references not supported #define SRT_REJX_NOROOM 1507 // The data stream cannot be archived due to lacking storage space. This is in case when the request type was to send a file or the live stream to be archived. // CODE NOT IN USE 508: unused: no redirection supported diff --git a/srtcore/core.cpp b/srtcore/core.cpp index a065fa3c3..ed6e3c9c0 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4441,9 +4441,12 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx } HLOGC(cnlog.Debug, log << CONID() << "processConnectResponse: HS RECEIVED: " << m_ConnRes.show()); - if (m_ConnRes.m_iReqType > URQ_FAILURE_TYPES) + if (m_ConnRes.m_iReqType >= URQ_FAILURE_TYPES) { m_RejectReason = RejectReasonForURQ(m_ConnRes.m_iReqType); + LOGC(cnlog.Warn, + log << CONID() << "processConnectResponse: rejecting per reception of a rejection HS response: " + << RequestTypeStr(m_ConnRes.m_iReqType)); return CONN_REJECT; } @@ -4626,6 +4629,7 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // in rendezvous it's completed before calling this function. if (!rendezvous) { + HLOGC(cnlog.Debug, log << CONID() << boolalpha << "postConnect: packet:" << bool(pResponse) << " rendezvous:" << rendezvous); // The "local storage depleted" case shouldn't happen here, but // this is a theoretical path that needs prevention. bool ok = pResponse; diff --git a/srtcore/srt.h b/srtcore/srt.h index d5a5eb196..9d7fb966c 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -922,6 +922,7 @@ SRT_API int srt_setrejectreason(SRTSOCKET sock, int value); // Planned removal: v1.6.0. SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg []; SRT_API const char* srt_rejectreason_str(int id); +SRT_API const char* srt_rejectreasonx_str(int id); SRT_API uint32_t srt_getversion(void); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index b3b67d327..827a3848a 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -17,7 +17,10 @@ written by #include #include +#include +#include #include "srt.h" +#include "access_control.h" #include "common.h" #include "packet.h" #include "core.h" @@ -470,6 +473,11 @@ extern const char* const srt_rejectreason_msg[] = { const char* srt_rejectreason_str(int id) { + if (id == SRT_REJX_FALLBACK) + { + return "Application fallback (default) rejection reason"; + } + if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -481,4 +489,58 @@ const char* srt_rejectreason_str(int id) return srt_rejection_reason_msg[id]; } +// NOTE: values in the first field must be sorted by numbers. +pair srt_rejectionx_reason_msg [] = { + + // Internal + make_pair(SRT_REJX_FALLBACK, "Default fallback reason"), + make_pair(SRT_REJX_KEY_NOTSUP, "Unsupported streamid key"), + make_pair(SRT_REJX_FILEPATH, "Incorrect resource path"), + make_pair(SRT_REJX_HOSTNOTFOUND, "Unrecognized host under h key"), + + // HTTP adopted codes + make_pair(SRT_REJX_BAD_REQUEST, "Bad request"), + make_pair(SRT_REJX_UNAUTHORIZED, "Unauthorized"), + make_pair(SRT_REJX_OVERLOAD, "Server overloaded or underpaid"), + make_pair(SRT_REJX_FORBIDDEN, "Resource access forbidden"), + make_pair(SRT_REJX_BAD_MODE, "Bad mode specified with m key"), + make_pair(SRT_REJX_UNACCEPTABLE, "Unacceptable parameters for specified resource"), + make_pair(SRT_REJX_CONFLICT, "Access conflict for a locked resource"), + make_pair(SRT_REJX_NOTSUP_MEDIA, "Unsupported media type specified with t key"), + make_pair(SRT_REJX_LOCKED, "Resource locked for any access"), + make_pair(SRT_REJX_FAILED_DEPEND, "Dependent session id expired"), + make_pair(SRT_REJX_ISE, "Internal server error"), + make_pair(SRT_REJX_GW, "Gateway target rejected connection"), + make_pair(SRT_REJX_DOWN, "Service is down for maintenance"), + make_pair(SRT_REJX_VERSION, "Unsupported version for the request"), + make_pair(SRT_REJX_NOROOM, "Storage capacity exceeded"), +}; + +struct FCompareItems +{ + bool operator()(const pair& a, int b) + { + return a.first < b; + } +}; + +const char* srt_rejectreasonx_str(int id) +{ + if (id < SRT_REJX_FALLBACK) + { + return "System-defined rejection reason (not extended)"; + } + + pair* begin = srt_rejectionx_reason_msg; + pair* end = begin + Size(srt_rejectionx_reason_msg); + pair* found = lower_bound(begin, end, id, FCompareItems()); + + if (found == end || found->first != id) + { + return "Undefined extended rejection code"; + } + + return found->second; +} + } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b07f92ae8..acb63109a 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -99,6 +99,19 @@ string DirectionName(SRT_EPOLL_T direction) return dir_name; } +static string RejectReasonStr(int id) +{ + if (id < SRT_REJC_PREDEFINED) + return srt_rejectreason_str(id); + + if (id < SRT_REJC_USERDEFINED) + return srt_rejectreasonx_str(id); + + ostringstream sout; + sout << "User-defined reason code " << id; + return sout.str(); +} + template inline bytevector FileRead(FileBase& ifile, size_t chunk, const string& filename) { @@ -1054,7 +1067,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1178,7 +1191,7 @@ void SrtCommon::OpenGroupClient() out << "[" << c.token << "] " << c.host << ":" << c.port; if (!c.source.empty()) out << "[[" << c.source.str() << "]]"; - out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl; + out << ": " << srt_strerror(c.error, 0) << ": " << RejectReasonStr(c.reason) << endl; } reasons.insert(c.reason); } @@ -1387,11 +1400,11 @@ void SrtCommon::Error(string src, int reason, int force_result) if ( Verbose::on ) Verb() << "FAILURE\n" << src << ": [" << result << "] " << "Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); else cerr << "\nERROR #" << result << ": Connection rejected: [" << int(reason) << "]: " - << srt_rejectreason_str(reason); + << RejectReasonStr(reason); } else { From 09d480d55b5de6a31549cb4f1658891538662455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 9 Jan 2023 21:01:40 +0100 Subject: [PATCH 057/517] Fixed testing app to set unauthorized rejection code for a better example --- testing/srt-test-live.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 7bf6bb51c..ed8036e11 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -71,11 +71,11 @@ #include "testmedia.hpp" // requires access to SRT-dependent globals #include "verbose.hpp" -// NOTE: This is without "haisrt/" because it uses an internal path +// NOTE: This is without "srt/" because it uses an internal path // to the library. Application using the "installed" library should // use #include -#include // This TEMPORARILY contains extra C++-only SRT API. +#include #include using namespace std; @@ -354,6 +354,7 @@ extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const soc // This hook sets the password to the just accepted socket // depending on the user + srt_setrejectreason(acpsock, SRT_REJX_UNAUTHORIZED); string exp_pw = passwd.at(username); srt_setsockflag(acpsock, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()); From 08d1e5cfcf27b0dc5376ef5a4507854a5293a552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Jan 2023 09:26:31 +0100 Subject: [PATCH 058/517] Minor fixes --- srtcore/api.cpp | 2 +- srtcore/buffer_rcv.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e18407238..84599efb2 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -229,7 +229,7 @@ srt::CUDTUnited::~CUDTUnited() string srt::CUDTUnited::CONID(SRTSOCKET sock) { - if (int32_t(sock) <= 0) // both SRT_INVALID_SOCK and SRT_SOCKID_CONNREQ and illegal negative domain + if (int32_t(sock) <= 0) // embraces SRT_INVALID_SOCK, SRT_SOCKID_CONNREQ and illegal negative domain return ""; std::ostringstream os; diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 373d7da11..4d06684fd 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -262,10 +262,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL { - if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. - { - HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with mflag field set to a negative value!"); - } IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno); int minDroppedOffset = -1; int iDropCnt = 0; @@ -303,6 +299,10 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno) } return iDropCnt; } + if (msgno < 0) // Note that only SRT_MSGNO_CONTROL is allowed in the protocol. + { + HLOGC(rbuflog.Error, log << "EPE: received UMSG_DROPREQ with msgflag field set to a negative value!"); + } // Drop by packet seqno range. const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); From b5dd5a565b2941ec316b8683777bc88c22e6231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 31 Jan 2023 15:57:09 +0100 Subject: [PATCH 059/517] [REFAX] Refactored function calls at close socket for preparing a change --- srtcore/api.cpp | 60 ++++++++++++++++++++++++++++++++----------------- srtcore/api.h | 9 ++++++++ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 97236fdbc..dbc339d0c 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -182,7 +182,6 @@ srt::CUDTUnited::CUDTUnited() , m_InitLock() , m_iInstanceCount(0) , m_bGCStatus(false) - , m_ClosedSockets() { // Socket ID MUST start from a random value m_SocketIDGenerator = genRandomInt(1, MAX_SOCKET_VAL); @@ -469,6 +468,16 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) return ns->m_SocketID; } +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, bool lateremove) +{ + m_ClosedSockets[id] = s; + if (!lateremove) + { + m_Sockets.erase(id); + } +} + int srt::CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer, const CPacket& hspkt, @@ -826,8 +835,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, ns->removeFromGroup(true); } #endif - m_Sockets.erase(id); - m_ClosedSockets[id] = ns; + swipeSocket_LOCKED(id, ns); } return -1; @@ -2020,8 +2028,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } #endif - m_Sockets.erase(s->m_SocketID); - m_ClosedSockets[s->m_SocketID] = s; + swipeSocket_LOCKED(s->m_SocketID, s); HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); CGlobEvent::triggerEvent(); @@ -2622,8 +2629,9 @@ void srt::CUDTUnited::checkBrokenSockets() // close broken connections and start removal timer s->setClosed(); + // NOTE: removal from m_Sockets POSTPONED in tbc. tbc.push_back(i->first); - m_ClosedSockets[i->first] = s; + swipeSocket_LOCKED(i->first, s, true); // remove from listener's queue sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); @@ -2716,9 +2724,6 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) s->removeFromGroup(true); } #endif - // decrease multiplexer reference count, and remove it if necessary - const int mid = s->m_iMuxID; - { ScopedLock cg(s->m_AcceptLock); @@ -2739,8 +2744,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; as->breakSocket_LOCKED(); - m_ClosedSockets[*q] = as; - m_Sockets.erase(*q); + swipeSocket_LOCKED(*q, as); } } @@ -2765,13 +2769,19 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); s->core().closeInternal(); + removeMux(s); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; - HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer."); +} - if (mid == -1) +// decrease multiplexer reference count, and remove it if necessary +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::removeMux(CUDTSocket* s) +{ + int mid = s->m_iMuxID; + if (mid == -1) // Ignore those already removed { - HLOGC(smlog.Debug, log << "GC/removeSocket: no muxer found, finishing."); + HLOGC(smlog.Debug, log << "removeMux: @" << s->m_SocketID << " has no muxer, ok."); return; } @@ -2779,19 +2789,19 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) m = m_mMultiplexer.find(mid); if (m == m_mMultiplexer.end()) { - LOGC(smlog.Fatal, log << "IPE: For socket @" << u << " MUXER id=" << mid << " NOT FOUND!"); + LOGC(smlog.Fatal, log << "IPE: For socket @" << s->m_SocketID << " MUXER id=" << mid << " NOT FOUND!"); return; } CMultiplexer& mx = m->second; mx.m_iRefCount--; - HLOGC(smlog.Debug, log << "unrefing underlying muxer " << mid << " for @" << u << ", ref=" << mx.m_iRefCount); - if (0 == mx.m_iRefCount) + HLOGC(smlog.Debug, log << "unrefing underlying muxer " << mid << " for @" << s->m_SocketID << ", ref=" << mx.m_iRefCount); + if (mx.m_iRefCount <= 0) { - HLOGC(smlog.Debug, - log << "MUXER id=" << mid << " lost last socket @" << u << " - deleting muxer bound to port " - << mx.m_pChannel->bindAddressAny().hport()); + HLOGC(smlog.Debug, log << "MUXER id=" << mid << " lost last socket @" + << s->m_SocketID << " - deleting muxer bound to " + << mx.m_pChannel->bindAddressAny().str()); // The channel has no access to the queues and // it looks like the multiplexer is the master of all of them. // The queues must be silenced before closing the channel @@ -2802,6 +2812,10 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) mx.destroy(); m_mMultiplexer.erase(m); } + else + { + HLOGC(smlog.Debug, log << "MUXER id=" << mid << " has still " << mx.m_iRefCount << " users"); + } } void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) @@ -3130,7 +3144,11 @@ void* srt::CUDTUnited::garbageCollect(void* p) s->removeFromGroup(false); } #endif - self->m_ClosedSockets[i->first] = s; + + // NOTE: not removing the socket from m_Sockets. + // This is a loop over m_Sockets and after this loop ends, + // this whole container will be cleared. + self->swipeSocket_LOCKED(i->first, s, true); // remove from listener's queue sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); diff --git a/srtcore/api.h b/srtcore/api.h index 551590e3e..6a1a9a8d5 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -256,6 +256,14 @@ class CUDTUnited /// @return The new UDT socket ID, or INVALID_SOCK. SRTSOCKET newSocket(CUDTSocket** pps = NULL); + /// Removes the socket from the global socket container + /// and place it in the socket trashcan. The socket should + /// remain there until all still pending activities are + /// finished and there are no more users of this socket. + /// Note that the swiped socket is no longer dispatchable + /// by id. + void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, bool lateremove = false); + /// Create (listener-side) a new socket associated with the incoming connection request. /// @param [in] listen the listening socket ID. /// @param [in] peer peer address. @@ -446,6 +454,7 @@ class CUDTUnited #endif void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL); bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls); + void removeMux(CUDTSocket* s); // Utility functions for updateMux void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af); From b31342c6cf3070a7522b704789eab3297192cd64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 1 Feb 2023 15:42:07 +0100 Subject: [PATCH 060/517] Changed parameter spec to enum. Some cosmetic fixes --- srtcore/api.cpp | 15 ++++++++------- srtcore/api.h | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index dbc339d0c..9f65338ce 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -469,7 +469,7 @@ SRTSOCKET srt::CUDTUnited::newSocket(CUDTSocket** pps) } // [[using locked(m_GlobControlLock)]] -void srt::CUDTUnited::swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, bool lateremove) +void srt::CUDTUnited::swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, CUDTUnited::SwipeSocketTerm lateremove) { m_ClosedSockets[id] = s; if (!lateremove) @@ -835,7 +835,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, ns->removeFromGroup(true); } #endif - swipeSocket_LOCKED(id, ns); + swipeSocket_LOCKED(id, ns, SWIPE_NOW); } return -1; @@ -2028,7 +2028,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) } #endif - swipeSocket_LOCKED(s->m_SocketID, s); + swipeSocket_LOCKED(s->m_SocketID, s, SWIPE_NOW); HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); CGlobEvent::triggerEvent(); @@ -2629,9 +2629,10 @@ void srt::CUDTUnited::checkBrokenSockets() // close broken connections and start removal timer s->setClosed(); - // NOTE: removal from m_Sockets POSTPONED in tbc. tbc.push_back(i->first); - swipeSocket_LOCKED(i->first, s, true); + + // NOTE: removal from m_SocketID POSTPONED. + swipeSocket_LOCKED(i->first, s, SWIPE_LATER); // remove from listener's queue sockets_t::iterator ls = m_Sockets.find(s->m_ListenSocket); @@ -2744,7 +2745,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; as->breakSocket_LOCKED(); - swipeSocket_LOCKED(*q, as); + swipeSocket_LOCKED(*q, as, SWIPE_NOW); } } @@ -3148,7 +3149,7 @@ void* srt::CUDTUnited::garbageCollect(void* p) // NOTE: not removing the socket from m_Sockets. // This is a loop over m_Sockets and after this loop ends, // this whole container will be cleared. - self->swipeSocket_LOCKED(i->first, s, true); + self->swipeSocket_LOCKED(i->first, s, self->SWIPE_LATER); // remove from listener's queue sockets_t::iterator ls = self->m_Sockets.find(s->m_ListenSocket); diff --git a/srtcore/api.h b/srtcore/api.h index 6a1a9a8d5..e5f72d4d7 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -256,13 +256,14 @@ class CUDTUnited /// @return The new UDT socket ID, or INVALID_SOCK. SRTSOCKET newSocket(CUDTSocket** pps = NULL); - /// Removes the socket from the global socket container - /// and place it in the socket trashcan. The socket should - /// remain there until all still pending activities are - /// finished and there are no more users of this socket. - /// Note that the swiped socket is no longer dispatchable - /// by id. - void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, bool lateremove = false); + enum SwipeSocketTerm { SWIPE_LATER, SWIPE_NOW }; + /// Removes the socket from the global socket container + /// and place it in the socket trashcan. The socket should + /// remain there until all still pending activities are + /// finished and there are no more users of this socket. + /// Note that the swiped socket is no longer dispatchable + /// by id. + void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, SwipeSocketTerm); /// Create (listener-side) a new socket associated with the incoming connection request. /// @param [in] listen the listening socket ID. From 4da035a65541251f8feb944630072d273d763c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 2 Feb 2023 10:04:54 +0100 Subject: [PATCH 061/517] Fixed wrong enum spec --- srtcore/api.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/srtcore/api.h b/srtcore/api.h index e5f72d4d7..1f2959c00 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -256,14 +256,14 @@ class CUDTUnited /// @return The new UDT socket ID, or INVALID_SOCK. SRTSOCKET newSocket(CUDTSocket** pps = NULL); - enum SwipeSocketTerm { SWIPE_LATER, SWIPE_NOW }; - /// Removes the socket from the global socket container - /// and place it in the socket trashcan. The socket should - /// remain there until all still pending activities are - /// finished and there are no more users of this socket. - /// Note that the swiped socket is no longer dispatchable - /// by id. - void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, SwipeSocketTerm); + enum SwipeSocketTerm { SWIPE_NOW = 0, SWIPE_LATER = 1 }; + /// Removes the socket from the global socket container + /// and place it in the socket trashcan. The socket should + /// remain there until all still pending activities are + /// finished and there are no more users of this socket. + /// Note that the swiped socket is no longer dispatchable + /// by id. + void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, SwipeSocketTerm); /// Create (listener-side) a new socket associated with the incoming connection request. /// @param [in] listen the listening socket ID. From b53a091777719f9594efba9d6f0ed1582839f1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 1 Mar 2023 16:41:36 +0100 Subject: [PATCH 062/517] [FIX] Calculate correctly max payload size per UDP packet --- apps/srt-file-transmit.cpp | 7 +- apps/transmitmedia.cpp | 37 ++++++++++- srtcore/api.cpp | 17 +++-- srtcore/buffer_rcv.cpp | 9 ++- srtcore/buffer_snd.cpp | 3 +- srtcore/buffer_snd.h | 2 +- srtcore/buffer_tools.cpp | 5 +- srtcore/buffer_tools.h | 3 +- srtcore/channel.cpp | 9 +++ srtcore/common.cpp | 2 +- srtcore/common.h | 9 +++ srtcore/congctl.cpp | 5 +- srtcore/core.cpp | 97 +++++++++++++++++++++------ srtcore/core.h | 30 ++++++--- srtcore/group.cpp | 15 +++-- srtcore/group_backup.h | 1 + srtcore/packet.h | 18 +++-- srtcore/packetfilter_api.h | 2 +- srtcore/socketconfig.cpp | 89 +++++++++++++++---------- srtcore/socketconfig.h | 2 + srtcore/srt.h | 7 +- srtcore/stats.h | 83 ++++++++++++++++++----- srtcore/window.cpp | 8 +-- srtcore/window.h | 31 +++++++-- test/test_fec_rebuilding.cpp | 8 +-- test/test_file_transmission.cpp | 113 +++++++++++++++++++++++++++++++- testing/srt-test-mpbond.cpp | 2 +- testing/testmedia.cpp | 45 ++++++++++--- 28 files changed, 523 insertions(+), 136 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..e29ae3248 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -180,7 +180,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) return 2; } - cfg.chunk_size = stoul(Option(params, "1456", o_chunk)); + cfg.chunk_size = stoul(Option(params, "0", o_chunk)); cfg.skip_flushing = Option(params, false, o_no_flush); cfg.bw_report = stoi(Option(params, "0", o_bwreport)); cfg.stats_report = stoi(Option(params, "0", o_statsrep)); @@ -681,8 +681,11 @@ int main(int argc, char** argv) // // Set global config variables // - if (cfg.chunk_size != SRT_LIVE_MAX_PLSIZE) + if (cfg.chunk_size != 0) transmit_chunk_size = cfg.chunk_size; + else + transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET; + transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf); transmit_bw_report = cfg.bw_report; transmit_stats_report = cfg.stats_report; diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 260323ef3..42970d92f 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -44,7 +44,7 @@ bool g_stats_are_printed_to_stdout = false; bool transmit_total_stats = false; unsigned long transmit_bw_report = 0; unsigned long transmit_stats_report = 0; -unsigned long transmit_chunk_size = SRT_LIVE_MAX_PLSIZE; +unsigned long transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET6; class FileSource: public Source { @@ -179,6 +179,36 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } + int fam_to_limit_size = AF_INET6; // take the less one as default + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + fam_to_limit_size = host_sa.family(); + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + } + + if (adapter != "" && adapter != host) + { + adapter_sa = CreateAddr(adapter); + fam_to_limit_size = adapter_sa.family(); + + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + } + + if (fam_to_limit_size != AF_INET) + fam_to_limit_size = AF_INET6; + if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { m_tsbpdmode = false; @@ -195,8 +225,9 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); + if (transmit_chunk_size > size_limit) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 63dab3927..4a22cb8ec 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -618,7 +618,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // bind to the same addr of listening socket - ns->core().open(); + ns->core().open(peer.family()); if (!updateListenerMux(ns, ls)) { // This is highly unlikely if not impossible, but there's @@ -928,7 +928,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - s->core().open(); + s->core().open(name.family()); updateMux(s, name); s->m_Status = SRTS_OPENED; @@ -957,7 +957,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->core().open(); + s->core().open(name.family()); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; @@ -1847,7 +1847,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i // same thing as bind() does, just with empty address so that // the binding parameters are autoselected. - s->core().open(); + s->core().open(target_addr.family()); sockaddr_any autoselect_sa(target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). @@ -3157,7 +3157,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; - m.m_pRcvQueue->init(128, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + // We can't use maxPayloadSize() because this value isn't valid until the connection is established. + // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. + size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + + HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 + << " plsize=" << payload_size << " hsize=" << 1024); + m.m_pRcvQueue->init(128, payload_size, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); // Rewrite the port here, as it might be only known upon return // from CChannel::open. diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 3098407a6..a2a3d3d65 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -130,7 +130,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) - , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) + , m_uAvgPayloadSz(0) { SRT_ASSERT(size < size_t(std::numeric_limits::max())); // All position pointers are integers } @@ -751,7 +751,12 @@ void CRcvBuffer::countBytes(int pkts, int bytes) m_iBytesCount += bytes; // added or removed bytes from rcv buffer m_iPktsCount += pkts; if (bytes > 0) // Assuming one pkt when adding bytes - m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + { + if (!m_uAvgPayloadSz) + m_uAvgPayloadSz = bytes; + else + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + } } void CRcvBuffer::releaseUnitInPos(int pos) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 26f885dd6..9f224a678 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -64,7 +64,7 @@ using namespace std; using namespace srt_logging; using namespace sync; -CSndBuffer::CSndBuffer(int size, int maxpld, int authtag) +CSndBuffer::CSndBuffer(int ip_family, int size, int maxpld, int authtag) : m_BufLock() , m_pBlock(NULL) , m_pFirstBlock(NULL) @@ -77,6 +77,7 @@ CSndBuffer::CSndBuffer(int size, int maxpld, int authtag) , m_iAuthTagSize(authtag) , m_iCount(0) , m_iBytesCount(0) + , m_rateEstimator(ip_family) { // initial physical buffer of "size" m_pBuffer = new Buffer; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 4440b9bfd..5da8c9631 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -87,7 +87,7 @@ class CSndBuffer /// @param size initial number of blocks (each block to store one packet payload). /// @param maxpld maximum packet payload (including auth tag). /// @param authtag auth tag length in bytes (16 for GCM, 0 otherwise). - CSndBuffer(int size = 32, int maxpld = 1500, int authtag = 0); + CSndBuffer(int ip_family, int size, int maxpld, int authtag); ~CSndBuffer(); public: diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 1cdc72d58..e0ecb81fb 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -102,11 +102,12 @@ void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); } -CRateEstimator::CRateEstimator() +CRateEstimator::CRateEstimator(int family) : m_iInRatePktsCount(0) , m_iInRateBytesCount(0) , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) + , m_iFullHeaderSize(CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE) {} void CRateEstimator::setInputRateSmpPeriod(int period) @@ -141,7 +142,7 @@ void CRateEstimator::updateInputRate(const time_point& time, int pkts, int bytes if (early_update || period_us > m_InRatePeriod) { // Required Byte/sec rate (payload + headers) - m_iInRateBytesCount += (m_iInRatePktsCount * CPacket::SRT_DATA_HDR_SIZE); + m_iInRateBytesCount += (m_iInRatePktsCount * m_iFullHeaderSize); m_iInRateBps = (int)(((int64_t)m_iInRateBytesCount * 1000000) / period_us); HLOGC(bslog.Debug, log << "updateInputRate: pkts:" << m_iInRateBytesCount << " bytes:" << m_iInRatePktsCount diff --git a/srtcore/buffer_tools.h b/srtcore/buffer_tools.h index a6d81a356..828690b95 100644 --- a/srtcore/buffer_tools.h +++ b/srtcore/buffer_tools.h @@ -93,7 +93,7 @@ class CRateEstimator typedef sync::steady_clock::time_point time_point; typedef sync::steady_clock::duration duration; public: - CRateEstimator(); + CRateEstimator(int family); public: uint64_t getInRatePeriod() const { return m_InRatePeriod; } @@ -125,6 +125,7 @@ class CRateEstimator time_point m_tsInRateStartTime; uint64_t m_InRatePeriod; // usec int m_iInRateBps; // Input Rate in Bytes/sec + int m_iFullHeaderSize; }; } diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 31a29092f..ec3c68beb 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1005,6 +1005,15 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet if ((msg_flags & errmsgflg[i].first) != 0) flg << " " << errmsgflg[i].second; + if (msg_flags & MSG_TRUNC) + { + // Additionally show buffer information in this case + flg << " buffers: "; + for (size_t i = 0; i < CPacket::PV_SIZE; ++i) + { + flg << "[" << w_packet.m_PacketVector[i].iov_len << "] "; + } + } // This doesn't work the same way on Windows, so on Windows just skip it. #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 53148136a..a39312ec3 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -209,7 +209,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc { // Check if the peer address is a model of IPv4-mapped-on-IPv6. // If so, it means that the `ip` array should be interpreted as IPv4. - const bool is_mapped_ipv4 = checkMappedIPv4((uint16_t*)peer.sin6.sin6_addr.s6_addr); + const bool is_mapped_ipv4 = checkMappedIPv4(peer.sin6); sockaddr_in6* a = (&w_addr.sin6); diff --git a/srtcore/common.h b/srtcore/common.h index 5f3dd7f18..7dc84ea14 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1420,6 +1420,15 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(const std::string& s, SrtConfig& w_config); +bool checkMappedIPv4(const uint16_t* sa); + +inline bool checkMappedIPv4(const sockaddr_in6& sa) +{ + const uint16_t* addr = reinterpret_cast(&sa.sin6_addr.s6_addr); + return checkMappedIPv4(addr); +} + + } // namespace srt #endif diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 91c73d660..b9265c046 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -63,6 +63,7 @@ class LiveCC: public SrtCongestionControlBase int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; + size_t m_zHeaderSize; // NAKREPORT stuff. int m_iMinNakInterval_us; // Minimum NAK Report Period (usec) @@ -81,6 +82,8 @@ class LiveCC: public SrtCongestionControlBase m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; + m_zHeaderSize = parent->m_config.iMSS - parent->maxPayloadSize(); + m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) @@ -173,7 +176,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod << " by avg pktsize=" << m_zSndAvgPayloadSize diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e6da157a5..b4d6160cf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -269,6 +269,7 @@ void srt::CUDT::construct() m_pSndQueue = NULL; m_pRcvQueue = NULL; + m_TransferIPVersion = AF_UNSPEC; // Will be set after connection m_pSNode = NULL; m_pRNode = NULL; @@ -305,7 +306,9 @@ void srt::CUDT::construct() // m_cbPacketArrival.set(this, &CUDT::defaultPacketArrival); } -srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent) + : m_parent(parent) + , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -328,7 +331,9 @@ srt::CUDT::CUDT(CUDTSocket* parent): m_parent(parent) } -srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor): m_parent(parent) +srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor) + : m_parent(parent) + , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -468,13 +473,17 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int); break; + // For SNDBUF/RCVBUF values take the variant that uses more memory. + // It is not possible to make sure what "family" is in use without + // checking if the socket is bound. This will also be the exact size + // of the memory in use. case SRTO_SNDBUF: - *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::UDP_HDR_SIZE); + *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); optlen = sizeof(int); break; @@ -756,7 +765,7 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) case SRTO_PAYLOADSIZE: optlen = sizeof(int); - *(int *)optval = (int) m_config.zExpPayloadSize; + *(int *)optval = (int) payloadSize(); break; case SRTO_KMREFRESHRATE: @@ -872,11 +881,15 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. // Initial sequence number, loss, acknowledgement, etc. -void srt::CUDT::clearData() +void srt::CUDT::clearData(int family) { - m_iMaxSRTPayloadSize = m_config.iMSS - CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_SndTimeWindow.initialize(full_hdr_size, m_iMaxSRTPayloadSize); + m_RcvTimeWindow.initialize(full_hdr_size, m_iMaxSRTPayloadSize); + m_iEXPCount = 1; m_iBandwidth = 1; // pkts/sec // XXX use some constant for this 16 @@ -919,11 +932,11 @@ void srt::CUDT::clearData() m_tsRcvPeerStartTime = steady_clock::time_point(); } -void srt::CUDT::open() +void srt::CUDT::open(int family) { ScopedLock cg(m_ConnectionLock); - clearData(); + clearData(family); // structures for queue if (m_pSNode == NULL) @@ -3025,7 +3038,9 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) m_config.sPacketFilterConfig.set(confstr); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; + // XXX Using less maximum payload size of IPv4 and IPv6; this is only about the payload size + // for live. + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET6 - cfg.extra_size; if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, @@ -4659,10 +4674,19 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } + m_TransferIPVersion = m_PeerAddr.family(); + if (m_PeerAddr.family() == AF_INET6) + { + // Check if the m_PeerAddr's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(m_PeerAddr.sin6)) + m_TransferIPVersion = AF_INET; + } + // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; + const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -5508,7 +5532,7 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } return iDropCnt; @@ -5532,7 +5556,7 @@ void srt::CUDT::setInitialRcvSeq(int32_t isn) const int iDropCnt = m_pRcvBuffer->dropAll(); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); sync::ScopedLock sl(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } m_pRcvBuffer->setStartSeqNo(isn); @@ -5572,8 +5596,30 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd try { + // XXX SND buffer may allocate more memory, but must set the size of a single + // packet that fits the transmission for the overall connection. For any mixed 4-6 + // connection it should be the less size, that is, for IPv6 + const int authtag = m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM ? HAICRYPT_AUTHTAG_MAX : 0; - m_pSndBuffer = new CSndBuffer(32, m_iMaxSRTPayloadSize, authtag); + + SRT_ASSERT(m_iMaxSRTPayloadSize != 0); + SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); + // IMPORTANT: + // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent + // over the current connection - which means that if any party is IPv6, then the maximum size + // is the one for IPv6 (1444). Only if both parties are IPv4, this maximum size is 1456. + // The family as the first argument is something different - it's for the header size in order + // to calculate rate and statistics. + + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); + + HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size + << " snd-bufsize=" << 32 << " TF-IPv" + << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") + << " authtag=" << authtag); + + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. @@ -5651,8 +5697,17 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& rewriteHandshakeData(peer, (w_hs)); - int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; - m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; + m_TransferIPVersion = peer.family(); + if (peer.family() == AF_INET6) + { + // Check if the peer's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(peer.sin6)) + m_TransferIPVersion = AF_INET; + } + + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); // Prepare all structures @@ -7285,7 +7340,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(m_TransferIPVersion == AF_UNSPEC ? AF_INET : m_TransferIPVersion); { ScopedLock statsguard(m_StatsLock); @@ -8861,7 +8916,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } } @@ -9956,8 +10011,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10158,7 +10213,7 @@ int srt::CUDT::processData(CUnit* in_unit) ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); + m_stats.rcvr.lost.count(stats::BytesPacketsCount(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " diff --git a/srtcore/core.h b/srtcore/core.h index ce0e53958..67ee0dcb7 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -179,7 +179,7 @@ class CUDT private: // constructor and desctructor void construct(); - void clearData(); + void clearData(int family); CUDT(CUDTSocket* parent); CUDT(CUDTSocket* parent, const CUDT& ancestor); const CUDT& operator=(const CUDT&) {return *this;} // = delete ? @@ -328,6 +328,19 @@ class CUDT int peerIdleTimeout_ms() const { return m_config.iPeerIdleTimeout_ms; } size_t maxPayloadSize() const { return m_iMaxSRTPayloadSize; } size_t OPT_PayloadSize() const { return m_config.zExpPayloadSize; } + size_t payloadSize() const + { + // If payloadsize is set, it should already be checked that + // it is less than the possible maximum payload size. So return it + // if it is set to nonzero value. In case when the connection isn't + // yet established, return also 0, if the value wasn't set. + if (m_config.zExpPayloadSize || !m_bConnected) + return m_config.zExpPayloadSize; + + // If SRTO_PAYLOADSIZE was remaining with 0 (default for FILE mode) + // then return the maximum payload size per packet. + return m_iMaxSRTPayloadSize; + } int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -446,7 +459,7 @@ class CUDT private: /// initialize a UDT entity and bind to a local address. - void open(); + void open(int family); /// Start listening to any connection request. void setListenState(); @@ -728,13 +741,6 @@ class CUDT static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); - CRateEstimator getRateEstimator() const - { - if (!m_pSndBuffer) - return CRateEstimator(); - return m_pSndBuffer->getRateEstimator(); - } - void setRateEstimator(const CRateEstimator& rate) { if (!m_pSndBuffer) @@ -1134,6 +1140,11 @@ class CUDT int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration + + CoreStats(int* payloadsize_loc) + : sndr(payloadsize_loc) + , rcvr(payloadsize_loc) + {} } m_stats; public: @@ -1168,6 +1179,7 @@ class CUDT sockaddr_any m_PeerAddr; // peer address sockaddr_any m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address + int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f4dfba1ba..c411740cd 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -236,7 +236,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); if (plsize == 0) - plsize = SRT_LIVE_MAX_PLSIZE; + plsize = CPacket::srtPayloadSize(data.agent.family()); // It is stated that the payload size // is taken from first, and every next one // will get the same. @@ -506,8 +506,8 @@ void CUDTGroup::deriveSettings(CUDT* u) IM(SRTO_FC, iFlightFlagSize); // Nonstandard - importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); - importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); + importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); IM(SRTO_LINGER, Linger); IM(SRTO_UDP_SNDBUF, iUDPSndBufSize); @@ -639,7 +639,7 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) case SRTO_SNDBUF: case SRTO_RCVBUF: - w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::UDP_HDR_SIZE)); + w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::udpHeaderSize(AF_INET))); break; case SRTO_LINGER: @@ -3533,7 +3533,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Only live streaming is supported - if (len > SRT_LIVE_MAX_PLSIZE) + // Also - as the group may use potentially IPv4 and IPv6 connections + // in the same group, use the size that fits both + if (len > SRT_MAX_PLSIZE_AF_INET6) { LOGC(gslog.Error, log << "grp/send(backup): buffer size=" << len << " exceeds maximum allowed in live mode"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -3968,7 +3970,8 @@ void CUDTGroup::internalKeepalive(SocketData* gli) } } -CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_LIVE_MAX_PLSIZE /*, 1000*/); +// Use the bigger size of SRT_MAX_PLSIZE to potentially fit both IPv4/6 +CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_MAX_PLSIZE_AF_INET /*, 1000*/); // Forwarder needed due to class definition order int32_t CUDTGroup::generateISN() diff --git a/srtcore/group_backup.h b/srtcore/group_backup.h index 790cf55ed..cecbc6d1b 100644 --- a/srtcore/group_backup.h +++ b/srtcore/group_backup.h @@ -79,6 +79,7 @@ namespace groups : m_stateCounter() // default init with zeros , m_activeMaxWeight() , m_standbyMaxWeight() + , m_rateEstimate(AF_INET6) // XXX Probably the whole solution is wrong { } diff --git a/srtcore/packet.h b/srtcore/packet.h index 027d5f0b3..b7399b925 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -364,16 +364,24 @@ class CPacket static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH_E_SIZE * sizeof(uint32_t) - // Can also be calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr). - static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. - - static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE; +private: // Do not disclose ingredients to the public + static const size_t UDP_HDR_SIZE = 8; // 8 bytes of UDP { u16 sport, dport, len, csum }. + static const size_t IPv4_HDR_SIZE = 20; // 20 bytes IPv4 + static const size_t IPv6_HDR_SIZE = 32; // 32 bytes IPv6 +public: + static inline size_t udpHeaderSize(int family) + { + return UDP_HDR_SIZE + (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE); + } + static inline size_t srtPayloadSize(int family) + { + return ETH_MAX_MTU_SIZE - (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE) - UDP_HDR_SIZE - HDR_SIZE; + } // Maximum transmission unit size. 1500 in case of Ethernet II (RFC 1191). static const size_t ETH_MAX_MTU_SIZE = 1500; // Maximum payload size of an SRT packet. - static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE; // Packet interface char* data() { return m_pcData; } diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 3bfba7c76..ef0d8867f 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -66,7 +66,7 @@ struct SrtFilterInitializer struct SrtPacket { uint32_t hdr[SRT_PH_E_SIZE]; - char buffer[SRT_LIVE_MAX_PLSIZE]; + char buffer[SRT_MAX_PLSIZE_AF_INET]; // Using this as the bigger one (this for AF_INET6 is smaller) size_t length; SrtPacket(size_t size): length(size) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 476162001..e72563c27 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -70,7 +70,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + if (ival < int(CPacket::udpHeaderSize(AF_INET6) + CHandShake::m_iContentSize)) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iMSS = ival; @@ -109,7 +109,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::UDP_HDR_SIZE); + co.iSndBufSize = bs / (co.iMSS - CPacket::udpHeaderSize(AF_INET)); } }; @@ -123,7 +123,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - CPacket::UDP_HDR_SIZE; + const int mssin_size = co.iMSS - CPacket::udpHeaderSize(AF_INET); if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; @@ -609,42 +609,20 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (val > SRT_LIVE_MAX_PLSIZE) + // We don't know at this point, how bit the payloadsize can be set, + // so we limit it to the biggest value of the two. + // When this payloadsize would be then too big to be used with given MSS and IPv6, + // this problem should be reported appropriately from srt_connect and srt_bind calls. + if (val > SRT_MAX_PLSIZE_AF_INET) { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU."); + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_MAX_PLSIZE_AF_INET << ", maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (!co.sPacketFilterConfig.empty()) + std::string errorlog; + if (!co.payloadSizeFits(size_t(val), AF_INET, (errorlog))) { - // This means that the filter might have been installed before, - // and the fix to the maximum payload size was already applied. - // This needs to be checked now. - SrtFilterConfig fc; - if (!ParseFilterConfig(co.sPacketFilterConfig.str(), fc)) - { - // Break silently. This should not happen - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - - const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << fc.extra_size - << " required for packet filter header"); - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - } - } - - // Not checking AUTO to allow defaul 1456 bytes. - if ((co.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX))) - { - LOGC(aclog.Error, - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."); + LOGP(aclog.Error, errorlog); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -840,7 +818,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET - fc.extra_size; if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Warn, @@ -1011,6 +989,47 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + if (!this->sPacketFilterConfig.empty()) + { + // This means that the filter might have been installed before, + // and the fix to the maximum payload size was already applied. + // This needs to be checked now. + SrtFilterConfig fc; + if (!this->sPacketFilterConfig.empty() && !ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) + { + // Break silently. This should not happen + w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return false; + } + + const size_t efc_max_payload_size = CPacket::srtPayloadSize(ip_family) - fc.extra_size; + if (size_t(val) > efc_max_payload_size) + { + std::ostringstream log; + log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) << " bytes decreased by " << fc.extra_size + << " required for packet filter header"; + w_errmsg = log.str(); + return false; + } + } + + // Not checking AUTO to allow defaul 1456 bytes. + if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + && (val > (CPacket::srtPayloadSize(ip_family) - HAICRYPT_AUTHTAG_MAX))) + { + std::ostringstream log; + log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) + << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX + << " required for AES-GCM."; + w_errmsg = log.str(); + return false; + } + + return true; +} + #if ENABLE_BONDING bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t optlen) { diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 488d12fb1..72ae9808f 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -326,6 +326,8 @@ struct CSrtConfig: CSrtMuxerConfig } int set(SRT_SOCKOPT optName, const void* val, int size); + + bool payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 39a90ce71..784274f86 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -299,7 +299,12 @@ static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS // This is the maximum payload size for Live mode, should you have a different // payload type than MPEG TS. -static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) +SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) + +static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) +static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) + +#define SRT_MAX_PLSIZE(famspec) ((famspec) == AF_INET ? SRT_MAX_PLSIZE_AF_INET : SRT_MAX_PLSIZE_AF_INET6) // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; diff --git a/srtcore/stats.h b/srtcore/stats.h index bce60761b..2c0276665 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -22,6 +22,8 @@ namespace stats class Packets { public: + typedef Packets count_type; + Packets() : m_count(0) {} Packets(uint32_t num) : m_count(num) {} @@ -46,27 +48,20 @@ class Packets uint32_t m_count; }; -class BytesPackets +class BytesPacketsCount { public: - BytesPackets() + BytesPacketsCount() : m_bytes(0) , m_packets(0) {} - BytesPackets(uint64_t bytes, uint32_t n = 1) + BytesPacketsCount(uint64_t bytes, uint32_t n = 1) : m_bytes(bytes) , m_packets(n) {} - BytesPackets& operator+= (const BytesPackets& other) - { - m_bytes += other.m_bytes; - m_packets += other.m_packets; - return *this; - } -public: void reset() { m_packets = 0; @@ -89,28 +84,63 @@ class BytesPackets return m_packets; } - uint64_t bytesWithHdr() const + BytesPacketsCount& operator+= (const BytesPacketsCount& other) { - return m_bytes + m_packets * CPacket::SRT_DATA_HDR_SIZE; + m_bytes += other.m_bytes; + m_packets += other.m_packets; + return *this; } -private: +protected: uint64_t m_bytes; uint32_t m_packets; }; -template +class BytesPackets: public BytesPacketsCount +{ +public: + typedef BytesPacketsCount count_type; + + BytesPackets() + : m_pPayloadSizeLocation(NULL) + {} + +public: + + void bindPayloadSize(int* sizeloc) + { + m_pPayloadSizeLocation = sizeloc; + } + + uint64_t bytesWithHdr() const + { + SRT_ASSERT(m_pPayloadSizeLocation != NULL); + size_t header_size = CPacket::ETH_MAX_MTU_SIZE - *m_pPayloadSizeLocation; + return m_bytes + m_packets * header_size; + } + +private: + int* m_pPayloadSizeLocation; +}; + +template struct Metric { METRIC_TYPE trace; METRIC_TYPE total; - void count(METRIC_TYPE val) + void count(typename METRIC_TYPE::count_type val) { trace += val; total += val; } + void bindPayloadSize(int* loc) + { + trace.bindPayloadSize(loc); + total.bindPayloadSize(loc); + } + void reset() { trace.reset(); @@ -137,6 +167,16 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. + Sender(int* payloadsize_loc) + { +#define BIND(var) var.bindPayloadSize(payloadsize_loc) + BIND(sent); + BIND(sentUnique); + BIND(sentRetrans); + BIND(dropped); +#undef BIND + } + void reset() { sent.reset(); @@ -180,6 +220,19 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. + Receiver(int* payloadsize_loc) + { +#define BIND(var) var.bindPayloadSize(payloadsize_loc) + BIND(recvd); + BIND(recvdUnique); + BIND(recvdRetrans); + BIND(lost); + BIND(dropped); + BIND(recvdBelated); + BIND(undecrypted); +#undef BIND + } + void reset() { recvd.reset(); diff --git a/srtcore/window.cpp b/srtcore/window.cpp index 46889ecb0..b038575e4 100644 --- a/srtcore/window.cpp +++ b/srtcore/window.cpp @@ -145,7 +145,7 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3 //////////////////////////////////////////////////////////////////////////////// -void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize) +void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize, size_t max_payload_size) { for (size_t i = 0; i < asize; ++ i) r_pktWindow[i] = 1000000; //1 sec -> 1 pkt/sec @@ -154,11 +154,11 @@ void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_p r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec for (size_t i = 0; i < asize; ++ i) - r_bytesWindow[i] = srt::CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i] + r_bytesWindow[i] = max_payload_size; //based on 1 pkt/sec set in r_pktWindow[i] } -int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps) +int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, size_t hdr_size, int& bytesps) { // get median value, but cannot change the original value order in the window std::copy(window, window + asize, replica); @@ -191,7 +191,7 @@ int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, // claculate speed, or return 0 if not enough valid value if (count > (asize >> 1)) { - bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received + bytes += (hdr_size * count); //Add protocol headers to bytes received bytesps = (int)ceil(1000000.0 / (double(sum) / double(bytes))); return (int)ceil(1000000.0 / (sum / count)); } diff --git a/srtcore/window.h b/srtcore/window.h index ecc4a4947..3dfd94c3e 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -130,10 +130,10 @@ class CACKWindow class CPktTimeWindowTools { public: - static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, int& bytesps); + static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, size_t hsize, int& bytesps); static int getBandwidth_in(const int* window, int* replica, size_t psize); - static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize); + static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize, size_t max_payload_size); }; template @@ -151,12 +151,13 @@ class CPktTimeWindow: CPktTimeWindowTools m_tsLastArrTime(sync::steady_clock::now()), m_tsCurrArrTime(), m_tsProbeTime(), - m_Probe1Sequence(SRT_SEQNO_NONE) + m_Probe1Sequence(SRT_SEQNO_NONE), + m_zPayloadSize(0), + m_zHeaderSize(0) { // Exception: up to CUDT ctor sync::setupMutex(m_lockPktWindow, "PktWindow"); sync::setupMutex(m_lockProbeWindow, "ProbeWindow"); - CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE); } ~CPktTimeWindow() @@ -174,11 +175,12 @@ class CPktTimeWindow: CPktTimeWindowTools int getPktRcvSpeed(int& w_bytesps) const { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Lock access to the packet Window sync::ScopedLock cg(m_lockPktWindow); int pktReplica[ASIZE]; // packet information window (inter-packet time) - return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, (w_bytesps)); + return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, m_zHeaderSize, (w_bytesps)); } int getPktRcvSpeed() const @@ -192,6 +194,7 @@ class CPktTimeWindow: CPktTimeWindowTools int getBandwidth() const { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Lock access to the packet Window sync::ScopedLock cg(m_lockProbeWindow); @@ -204,6 +207,7 @@ class CPktTimeWindow: CPktTimeWindowTools void onPktSent(int currtime) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); int interval = currtime - m_iLastSentTime; if ((interval < m_iMinPktSndInt) && (interval > 0)) @@ -216,6 +220,7 @@ class CPktTimeWindow: CPktTimeWindowTools void onPktArrival(int pktsz = 0) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); sync::ScopedLock cg(m_lockPktWindow); m_tsCurrArrTime = sync::steady_clock::now(); @@ -236,6 +241,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Shortcut to test a packet for possible probe 1 or 2 void probeArrival(const CPacket& pkt, bool unordered) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet @@ -257,6 +263,7 @@ class CPktTimeWindow: CPktTimeWindowTools /// Record the arrival time of the first probing packet. void probe1Arrival(const CPacket& pkt, bool unordered) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) { // Reset the starting probe into "undefined", when @@ -274,6 +281,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probe2Arrival(const CPacket& pkt) { + SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); // Reject probes that don't refer to the very next packet // towards the one that was lately notified by probe1Arrival. // Otherwise the result can be stupid. @@ -302,7 +310,7 @@ class CPktTimeWindow: CPktTimeWindowTools // record the probing packets interval // Adjust the time for what a complete packet would have take const int64_t timediff = sync::count_microseconds(m_tsCurrArrTime - m_tsProbeTime); - const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE; + const int64_t timediff_times_pl_size = timediff * m_zPayloadSize; // Let's take it simpler than it is coded here: // (stating that a packet has never zero size) @@ -327,6 +335,14 @@ class CPktTimeWindow: CPktTimeWindowTools m_iProbeWindowPtr = 0; } + // Late initialization because these sizes aren't known at construction time of the parent CUDT class. + void initialize(size_t h, size_t s) + { + m_zHeaderSize = h; + m_zPayloadSize = s; + CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE, s); + } + private: int m_aPktWindow[ASIZE]; // Packet information window (inter-packet time) int m_aBytesWindow[ASIZE]; @@ -345,6 +361,9 @@ class CPktTimeWindow: CPktTimeWindowTools sync::steady_clock::time_point m_tsProbeTime; // Arrival time of the first probing packet int32_t m_Probe1Sequence; // Sequence number for which the arrival time was notified + size_t m_zPayloadSize; + size_t m_zHeaderSize; + private: CPktTimeWindow(const CPktTimeWindow&); CPktTimeWindow &operator=(const CPktTimeWindow&); diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 46afd8981..bd50c6ffb 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -58,7 +58,7 @@ class TestFECRebuilding: public testing::Test source.emplace_back(new CPacket); CPacket& p = *source.back(); - p.allocate(SRT_LIVE_MAX_PLSIZE); + p.allocate(SRT_MAX_PLSIZE_AF_INET); uint32_t* hdr = p.getHeader(); @@ -729,7 +729,7 @@ TEST_F(TestFECRebuilding, Prepare) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -750,7 +750,7 @@ TEST_F(TestFECRebuilding, NoRebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -827,7 +827,7 @@ TEST_F(TestFECRebuilding, Rebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 5a646fb7d..48d790471 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -17,6 +17,7 @@ #endif #include "srt.h" +#include "netinet_any.h" #include #include @@ -27,7 +28,7 @@ //#pragma comment (lib, "ws2_32.lib") -TEST(Transmission, FileUpload) +TEST(FileTransmission, Upload) { srt_startup(); @@ -197,3 +198,113 @@ TEST(Transmission, FileUpload) (void)srt_cleanup(); } + +TEST(FileTransmission, Setup46) +{ + using namespace srt; + + srt_startup(); + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + try + { + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + // srt_setloglevel(LOG_DEBUG); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); + + + srt_setloglevel(LOG_DEBUG); + + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_port << "\n"; + break; + } + + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); + + srt_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + unsigned int randseed = std::time(NULL); + + for (size_t i = 0; i < SIZE; ++i) + buffer[i] = rand_r(&randseed); + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + + } + catch (...) + { + srt_cleanup(); + throw; + } + + srt_cleanup(); +} + +// XXX Setup66 - establish an IPv6 to IPv6 connection and make sure max payload size is that of IPv6. diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index c23b262b7..90baae7f6 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -240,7 +240,7 @@ int main( int argc, char** argv ) return 2; } - size_t chunk = SRT_LIVE_MAX_PLSIZE; + size_t chunk = SRT_MAX_PLSIZE_AF_INET; // state the bigger size // Now run the loop try diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 34d2bdc9b..b30d98a20 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -392,6 +392,37 @@ void SrtCommon::InitParameters(string host, string path, map par) { m_mode = par.at("mode"); } + + int fam_to_limit_size = AF_INET6; // take the less one as default + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + fam_to_limit_size = host_sa.family(); + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + } + + if (adapter != "") + { + adapter_sa = CreateAddr(adapter); + fam_to_limit_size = adapter_sa.family(); + + if (fam_to_limit_size == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + } + + if (fam_to_limit_size != AF_INET) + fam_to_limit_size = AF_INET6; + SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) { @@ -445,16 +476,14 @@ void SrtCommon::InitParameters(string host, string path, map par) // That's kinda clumsy, but it must rely on the defaults. // Default mode is live, so check if the file mode was enforced - if (par.count("transtype") == 0 || par["transtype"] != "file") + if ((par.count("transtype") == 0 || par["transtype"] != "file") + && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - // If the Live chunk size was nondefault, enforce the size. - if (transmit_chunk_size != SRT_LIVE_DEF_PLSIZE) - { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); + if (transmit_chunk_size > size_limit) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); - par["payloadsize"] = Sprint(transmit_chunk_size); - } + par["payloadsize"] = Sprint(transmit_chunk_size); } // Assigning group configuration from a special "groupconfig" attribute. From a8b91042845744053fa339937b6f437973d46591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 17:52:02 +0100 Subject: [PATCH 063/517] Applied verification of the payload size fit. Added tests. Updated documentation --- docs/API/API-functions.md | 16 +++ docs/API/API-socket-options.md | 256 +++++++++++++++++++++++---------- srtcore/api.cpp | 8 +- srtcore/core.cpp | 48 +++++-- srtcore/core.h | 17 ++- srtcore/socketconfig.cpp | 4 +- srtcore/socketconfig.h | 4 + srtcore/srt.h | 1 + srtcore/stats.h | 53 ++++--- test/test_ipv6.cpp | 144 ++++++++++++++++++- 10 files changed, 417 insertions(+), 134 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 4292a99b5..d4c4de970 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -172,6 +172,7 @@ Since SRT v1.5.0. | [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | | [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | | [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.6.0-dev | The connection was rejected due to an unsupported or mismatching encryption mode | +| [SRT_REJ_SETTINGS](#SRT_REJ_SETTINGS) | 1.6.0 | The connection was rejected because settings on both parties are in collision and cannot negotiate common values | | | | |

Error Codes

@@ -3064,6 +3065,21 @@ and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by the application. +#### SRT_REJ_CRYPTO + +Settings for `SRTO_CRYPTOMODE` on both parties are not compatible with one another. +See [`SRTO_CRYPTOMODE`](API-socket-options.md#SRTO_CRYPTOMODE) for details. + +#### SRT_REJ_SETTINGS + +Settings for various transmission parameters that are supposed to be negotiated +during the handshake (in order to agree upon a common value) are under restrictions +that make finding common values for them impossible. Cases include: + +* `SRTO_PAYLOADSIZE`, which is nonzero in live mode, is set to a value that +exceeds the free space in a single packet that results from the value of the +negotiated MSS value + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 5d32db4ca..2730dcc92 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -264,7 +264,7 @@ The following table lists SRT API socket options in alphabetical order. Option d ### Option Descriptions -#### SRTO_BINDTODEVICE +#### `SRTO_BINDTODEVICE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir |Entity| | --------------------- | ----- | -------- | -------- | ------ | -------- | ------ |-----|------| @@ -287,7 +287,7 @@ for a process that runs as root. Otherwise the function that applies the setting --- -#### SRTO_CONGESTION +#### `SRTO_CONGESTION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -309,7 +309,7 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST --- -#### SRTO_CONNTIMEO +#### `SRTO_CONNTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -323,7 +323,7 @@ will be 10 times the value set with `SRTO_CONNTIMEO`. --- -#### SRTO_CRYPTOMODE +#### `SRTO_CRYPTOMODE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | --------- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -377,7 +377,7 @@ There is no way to check the crypto mode being requested by the SRT caller at th --- -#### SRTO_DRIFTTRACER +#### `SRTO_DRIFTTRACER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -389,7 +389,7 @@ Enables or disables time drift tracer (receiver). --- -#### SRTO_ENFORCEDENCRYPTION +#### `SRTO_ENFORCEDENCRYPTION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -434,7 +434,7 @@ on the caller side. --- -#### SRTO_EVENT +#### `SRTO_EVENT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -449,7 +449,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of --- -#### SRTO_FC +#### `SRTO_FC` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -488,7 +488,7 @@ where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCV --- -#### SRTO_GROUPCONNECT +#### `SRTO_GROUPCONNECT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -514,7 +514,7 @@ function will return the group, not this socket ID. --- -#### SRTO_GROUPMINSTABLETIMEO +#### `SRTO_GROUPMINSTABLETIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -548,7 +548,7 @@ Note that the value of this option is not allowed to exceed the value of --- -#### SRTO_GROUPTYPE +#### `SRTO_GROUPTYPE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -567,7 +567,7 @@ context than inside the listener callback handler, the value is undefined. --- -#### SRTO_INPUTBW +#### `SRTO_INPUTBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -588,7 +588,7 @@ and keep the default 25% value for `SRTO_OHEADBW`*. --- -#### SRTO_MININPUTBW +#### `SRTO_MININPUTBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -603,7 +603,7 @@ See [`SRTO_INPUTBW`](#SRTO_INPUTBW). --- -#### SRTO_IPTOS +#### `SRTO_IPTOS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -621,7 +621,7 @@ and the actual value for connected sockets. --- -#### SRTO_IPTTL +#### `SRTO_IPTTL` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -639,7 +639,7 @@ and the actual value for connected sockets. --- -#### SRTO_IPV6ONLY +#### `SRTO_IPV6ONLY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -661,7 +661,7 @@ reliable way). Possible values are: --- -#### SRTO_ISN +#### `SRTO_ISN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -678,7 +678,7 @@ used in any regular development.* --- -#### SRTO_KMPREANNOUNCE +#### `SRTO_KMPREANNOUNCE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | @@ -712,7 +712,7 @@ The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. --- -#### SRTO_KMREFRESHRATE +#### `SRTO_KMREFRESHRATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | @@ -734,7 +734,7 @@ might still be in flight, or packets that have to be retransmitted. --- -#### SRTO_KMSTATE +#### `SRTO_KMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -752,7 +752,7 @@ for more details. --- -#### SRTO_LATENCY +#### `SRTO_LATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -771,7 +771,7 @@ be sender and receiver at the same time, and `SRTO_SENDER` became redundant. --- -#### SRTO_LINGER +#### `SRTO_LINGER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -789,7 +789,7 @@ The default value in [the file transfer configuration](./API.md#transmission-typ --- -#### SRTO_LOSSMAXTTL +#### `SRTO_LOSSMAXTTL` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -810,7 +810,7 @@ By default this value is set to 0, which means that this mechanism is off. --- -#### SRTO_MAXBW +#### `SRTO_MAXBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -834,7 +834,7 @@ therefore the default -1 remains even in live mode. --- -#### SRTO_MESSAGEAPI +#### `SRTO_MESSAGEAPI` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -873,7 +873,7 @@ SCTP protocol. --- -#### SRTO_MINVERSION +#### `SRTO_MINVERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -889,30 +889,91 @@ The default value is 0x010000 (SRT v1.0.0). --- -#### SRTO_MSS +#### `SRTO_MSS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -Maximum Segment Size. Used for buffer allocation and rate calculation using -packet counter assuming fully filled packets. Each party can set its own MSS -value independently. During a handshake the parties exchange MSS values, and -the lowest is used. - -*Generally on the internet MSS is 1500 by default. This is the maximum -size of a UDP packet and can be only decreased, unless you have some unusual -dedicated network settings. MSS is not to be confused with the size of the UDP -payload or SRT payload - this size is the size of the IP packet, including the -UDP and SRT headers* - -THe value of `SRTO_MSS` must not exceed `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. +Maximum Segment Size. This value represents the maximum size of a UDP packet +sent by the system. Therefore the value of `SRTO_MSS` must not exceed the +values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer +allocation and rate calculation using packet counter assuming fully filled +packets. + +This value is a sum of: + +* IP header (20 bytes for IPv4 or 32 bytes for IPv6) +* UDP header (8 bytes) +* SRT header (16 bytes) +* remaining space (as the maximum payload size available for a packet) + +For the default 1500 the "remaining space" part is effectively 1456 for IPv4 +and 1444 for IPv6. + +Note that the IP version used here is not the domain of the socket, but the +in-transmission IP version. This is IPv4 for a case when the current socket's +binding address is of IPv4 domain, or if it is IPv6, but the peer's address +is then an IPv6-mapped-IPv4 address. The in-transmission IPv6 is only if the +peer's address is a true IPv6 address. Hence it is not possible to deteremine +all these limitations until the connection is established. Parts of SRT that +must allocate any resources regarding this value are using the layout as per +IPv4 because this results in a greater size of "remaining space". + +This value can be set on both connection parties independently, but after +connection this option gets an effectively negotiated value, which is the less +one from both parties. + +This value then effectively controls: + +* The maximum size of the data in a single UDP packet ("remaining space"). + +* The size of the memory space allocated for a single packet in the sender +and receiver buffers. This value is equal to "SRT header" + "remaining space" +in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it +is that some buffer resources are allocated prior to the connection, so this +value must fit both IPv4 and IPv6 for buffer memory allocation. + +The default value 1500 matches the standard MTU size for network devices. It +is recommended that this value be set at maximum to the value of MTU size of +the network device that you will use for connection. + +Detailed recommendations for this value differ in the file and live mode. + +In the live mode a single call to `srt_send*` function may only send data +that fit in one packet. This size is defined by the `SRTO_PAYLOADSIZE` +option (defult: 1316) and it is also the size of the data in a single UDP +packet. To save memory space, you may want then to set MSS in live mode to +a value for which the "remaining space" matches `SRTO_PAYLOADSIZE` value (for +default 1316 it will be 1360 for IPv4 and 1372 for IPv6). This is not done by +default for security reasons: this may potentially lead to inability to read an +incoming UDP packet if its size is by some reason bigger than the negotiated MSS. +This may lead to misleading situations and hard to detect errors. You should +set such a value only if the peer is trusted (that is, you can be certain that +it will never come to a situation of having received an oversized UDP packet +over the link used for the connection). See also limitations for +`SRTO_PAYLOADSIZE`. + +In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for one single packet sending, and therefore bigger portions of data are +internally split into smaller portions, each one using the maximum available +"remaining space". The best value for this case is then equal to the current +network device's MTU size. Setting a greater value is possible (maximum for the +system API is 65535), but it may lead to packet fragmentation on the system +level. This is highly unwanted in SRT because: + +* SRT does also its own fragmentation, so it would be counter-productive +* It would use more system resources with no advantage +* SRT is unaware of it, so the statistics will be slightly falsified + +The system-level packet fragmentation cannot be however reliably turned off; +the best approach is then to avoid it by using appropriate parameters. [Return to list](#list-of-options) --- -#### SRTO_NAKREPORT +#### `SRTO_NAKREPORT` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -929,7 +990,7 @@ The default is true for Live mode, and false for File mode (see [`SRTO_TRANSTYPE --- -#### SRTO_OHEADBW +#### `SRTO_OHEADBW` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -959,7 +1020,7 @@ and break quickly at any rise in packet loss. --- -#### SRTO_PACKETFILTER +#### `SRTO_PACKETFILTER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1003,7 +1064,7 @@ Cases when negotiation succeeds: | fec,cols:10 | fec,cols:10,rows:20 | fec,cols:10,rows:20,arq:onreq,layout:even | fec,layout:staircase | fec,cols:10 | fec,cols:10,rows:1,arq:onreq,layout:staircase -In these cases the configuration is rejected with SRT_REJ_FILTER code: +In these cases the configuration is rejected with `SRT_REJ_FILTER` code: | Peer A | Peer B | Error reason |-----------------------|---------------------|-------------------------- @@ -1025,7 +1086,7 @@ For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-f --- -#### SRTO_PASSPHRASE +#### `SRTO_PASSPHRASE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1054,18 +1115,57 @@ encrypted connection, they have to simply set the same passphrase. --- -#### SRTO_PAYLOADSIZE +#### `SRTO_PAYLOADSIZE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | -Sets the maximum declared size of a single call to sending function in Live -mode. When set to 0, there's no limit for a single sending call. +Sets the data limitation mode and the maximum data size for sending at once. + +The default value is 0 in the file mode and 1316 in live mode (this is one of +the options modified together with `SRTO_TRANSTYPE`). + +If the value is 0, this means a "file mode", in which the call to `srt_send*` +is not limited to a size fitting in one single packet, that is, the supplied +data will be split into multiple pieces fitting in a single UDP packet, if +necessary, as well as every packet will use the maximum space available +in a UDP packet (except the last in the stream or in the message) according to +the `SRTO_MSS` setting and others that may influence this size (such as +`SRTO_PACKETFILTER` and `SRTO_CRYPTOMODE`). + +If the value is greater than 0, this means a "live mode", and the value +defines the maximum size of: + +* the single call to a sending function (`srt_send*`) +* the payload supplied in every single data packet + +This value can be set to a greater value than the default 1316, but the maximum +possible value is limited by the following factors: + +* 1500 is the default MSS (see `SRTO_MSS`), including headers, which are: + * 20 bytes for IPv4 or 32 bytes for IPv6 + * 8 bytes for UDP + * 16 bytes for SRT + +This alone gives the limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be however further decreased in the following cases: + +* 4 bytes reserved for FEC, if you use the builtin FEC packet filter (see `SRTO_PACKETFILTER`) +* 16 bytes reserved for authentication tag, if you use AES GCM (see `SRTO_CRYPTOMODE`) + +**WARNING**: The option setter will reject the setting if this value is too +great, but note that not every limitation can be checked prior to connection. +This includes: + +* MSS defined for the peer, which may override MSS set in the agent +* The in-transmission IP version - see `SRTO_MSS` for details -For Live mode: Default value is 1316, but can be increased up to 1456. Note that -with the `SRTO_PACKETFILTER` option additional header space is usually required, -which decreases the maximum possible value for `SRTO_PAYLOADSIZE`. +These values also influence the "remaining space" in the packet to be used for +payload. If during the handshake it turns out that this "remaining space" is +less than the value set for `SRTO_PAYLOADSIZE` (including when it remains with +the default value), the connection will be rejected with the `SRT_REJ_SETTINGS` +code. For File mode: Default value is 0 and it's recommended not to be changed. @@ -1073,7 +1173,7 @@ For File mode: Default value is 0 and it's recommended not to be changed. --- -#### SRTO_PBKEYLEN +#### `SRTO_PBKEYLEN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1160,7 +1260,7 @@ undefined behavior: --- -#### SRTO_PEERIDLETIMEO +#### `SRTO_PEERIDLETIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1174,7 +1274,7 @@ considered broken on timeout. --- -#### SRTO_PEERLATENCY +#### `SRTO_PEERLATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1195,7 +1295,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### SRTO_PEERVERSION +#### `SRTO_PEERVERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1209,7 +1309,7 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. --- -#### SRTO_RCVBUF +#### `SRTO_RCVBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1229,7 +1329,7 @@ than the Flight Flag size). --- -#### SRTO_RCVDATA +#### `SRTO_RCVDATA` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1241,7 +1341,7 @@ Size of the available data in the receive buffer. --- -#### SRTO_RCVKMSTATE +#### `SRTO_RCVKMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1255,7 +1355,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### SRTO_RCVLATENCY +#### `SRTO_RCVLATENCY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1297,7 +1397,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### SRTO_RCVSYN +#### `SRTO_RCVSYN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1334,7 +1434,7 @@ derived from the socket of which the group is a member). --- -#### SRTO_RCVTIMEO +#### `SRTO_RCVTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1348,7 +1448,7 @@ it will behave as if in "non-blocking mode". The -1 value means no time limit. --- -#### SRTO_RENDEZVOUS +#### `SRTO_RENDEZVOUS` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1361,7 +1461,7 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot --- -#### SRTO_RETRANSMITALGO +#### `SRTO_RETRANSMITALGO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | @@ -1389,7 +1489,7 @@ Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). --- -#### SRTO_REUSEADDR +#### `SRTO_REUSEADDR` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1416,7 +1516,7 @@ its address.* --- -#### SRTO_SENDER +#### `SRTO_SENDER` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1436,7 +1536,7 @@ parties simultaneously. --- -#### SRTO_SNDBUF +#### `SRTO_SNDBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1448,7 +1548,7 @@ Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. --- -#### SRTO_SNDDATA +#### `SRTO_SNDDATA` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1460,7 +1560,7 @@ Size of the unacknowledged data in send buffer. --- -#### SRTO_SNDDROPDELAY +#### `SRTO_SNDDROPDELAY` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1487,7 +1587,7 @@ always when requested). --- -#### SRTO_SNDKMSTATE +#### `SRTO_SNDKMSTATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1501,7 +1601,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### SRTO_SNDSYN +#### `SRTO_SNDSYN` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1527,7 +1627,7 @@ but will have no effect on the listener socket itself. --- -#### SRTO_SNDTIMEO +#### `SRTO_SNDTIMEO` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1541,7 +1641,7 @@ if in "non-blocking mode". The -1 value means no time limit. --- -#### SRTO_STATE +#### `SRTO_STATE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1553,7 +1653,7 @@ Returns the current socket state, same as `srt_getsockstate`. --- -#### SRTO_STREAMID +#### `SRTO_STREAMID` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1582,7 +1682,7 @@ influence anything. --- -#### SRTO_TLPKTDROP +#### `SRTO_TLPKTDROP` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1601,7 +1701,7 @@ enabled in sender if receiver supports it. --- -#### SRTO_TRANSTYPE +#### `SRTO_TRANSTYPE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1617,7 +1717,7 @@ Values defined by enum `SRT_TRANSTYPE` (see above for possible values) --- -#### SRTO_TSBPDMODE +#### `SRTO_TSBPDMODE` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1635,7 +1735,7 @@ the application. --- -#### SRTO_UDP_RCVBUF +#### `SRTO_UDP_RCVBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1648,7 +1748,7 @@ based on MSS value. Receive buffer must not be greater than FC size. --- -#### SRTO_UDP_SNDBUF +#### `SRTO_UDP_SNDBUF` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1661,7 +1761,7 @@ on `SRTO_MSS` value. --- -#### SRTO_VERSION +#### `SRTO_VERSION` | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 4a22cb8ec..e2dd288b7 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -618,7 +618,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } // bind to the same addr of listening socket - ns->core().open(peer.family()); + ns->core().open(); if (!updateListenerMux(ns, ls)) { // This is highly unlikely if not impossible, but there's @@ -928,7 +928,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - s->core().open(name.family()); + s->core().open(); updateMux(s, name); s->m_Status = SRTS_OPENED; @@ -957,7 +957,7 @@ int srt::CUDTUnited::bind(CUDTSocket* s, UDPSOCKET udpsock) // Successfully extracted, so update the size name.len = namelen; - s->core().open(name.family()); + s->core().open(); updateMux(s, name, &udpsock); s->m_Status = SRTS_OPENED; @@ -1847,7 +1847,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i // same thing as bind() does, just with empty address so that // the binding parameters are autoselected. - s->core().open(target_addr.family()); + s->core().open(); sockaddr_any autoselect_sa(target_addr.family()); // This will create such a sockaddr_any that // will return true from empty(). diff --git a/srtcore/core.cpp b/srtcore/core.cpp index b4d6160cf..c66492375 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -308,7 +308,6 @@ void srt::CUDT::construct() srt::CUDT::CUDT(CUDTSocket* parent) : m_parent(parent) - , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -333,7 +332,6 @@ srt::CUDT::CUDT(CUDTSocket* parent) srt::CUDT::CUDT(CUDTSocket* parent, const CUDT& ancestor) : m_parent(parent) - , m_stats(&m_iMaxSRTPayloadSize) { construct(); @@ -478,12 +476,12 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) // checking if the socket is bound. This will also be the exact size // of the memory in use. case SRTO_SNDBUF: - *(int *)optval = m_config.iSndBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); + *(int *)optval = m_config.iSndBufSize * m_config.bytesPerPkt(); optlen = sizeof(int); break; case SRTO_RCVBUF: - *(int *)optval = m_config.iRcvBufSize * (m_config.iMSS - CPacket::udpHeaderSize(AF_INET)); + *(int *)optval = m_config.iRcvBufSize * m_config.bytesPerPkt(); optlen = sizeof(int); break; @@ -881,9 +879,9 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // XXX REFACTOR: Make common code for CUDT constructor and clearData, // possibly using CUDT::construct. // Initial sequence number, loss, acknowledgement, etc. -void srt::CUDT::clearData(int family) +void srt::CUDT::clearData() { - const size_t full_hdr_size = CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -932,11 +930,11 @@ void srt::CUDT::clearData(int family) m_tsRcvPeerStartTime = steady_clock::time_point(); } -void srt::CUDT::open(int family) +void srt::CUDT::open() { ScopedLock cg(m_ConnectionLock); - clearData(family); + clearData(); // structures for queue if (m_pSNode == NULL) @@ -4185,7 +4183,7 @@ EConnectStatus srt::CUDT::processRendezvous( // This must be done before prepareConnectionObjects(), because it sets ISN and m_iMaxSRTPayloadSize needed to create buffers. if (!applyResponseSettings(pResponse)) { - LOGC(cnlog.Error, log << CONID() << "processRendezvous: rogue peer"); + LOGC(cnlog.Error, log << CONID() << "processRendezvous: peer settings rejected"); return CONN_REJECT; } @@ -4685,6 +4683,21 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; + + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_SETTINGS; + return false; + } + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_stats.setupHeaderSize(full_hdr_size); + + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; @@ -5676,6 +5689,20 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // Uses the smaller MSS between the peers m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS); + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; + m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_SETTINGS; + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } + + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_stats.setupHeaderSize(full_hdr_size); + // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; m_iPeerISN = w_hs.m_iISN; @@ -5706,9 +5733,6 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& m_TransferIPVersion = AF_INET; } - const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; - m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; - HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); // Prepare all structures if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0)) diff --git a/srtcore/core.h b/srtcore/core.h index 67ee0dcb7..ee79e3442 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -179,7 +179,7 @@ class CUDT private: // constructor and desctructor void construct(); - void clearData(int family); + void clearData(); CUDT(CUDTSocket* parent); CUDT(CUDTSocket* parent, const CUDT& ancestor); const CUDT& operator=(const CUDT&) {return *this;} // = delete ? @@ -341,6 +341,7 @@ class CUDT // then return the maximum payload size per packet. return m_iMaxSRTPayloadSize; } + int sndLossLength() { return m_pSndLossList->getLossLength(); } int32_t ISN() const { return m_iISN; } int32_t peerISN() const { return m_iPeerISN; } @@ -459,7 +460,7 @@ class CUDT private: /// initialize a UDT entity and bind to a local address. - void open(int family); + void open(); /// Start listening to any connection request. void setListenState(); @@ -1137,14 +1138,16 @@ class CUDT time_point tsLastSampleTime; // last performance sample time int traceReorderDistance; double traceBelatedTime; - + int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration - CoreStats(int* payloadsize_loc) - : sndr(payloadsize_loc) - , rcvr(payloadsize_loc) - {} + void setupHeaderSize(int hsize) + { + sndr.setupHeaderSize(hsize); + rcvr.setupHeaderSize(hsize); + } + } m_stats; public: diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index e72563c27..27eda173b 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -109,7 +109,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::udpHeaderSize(AF_INET)); + co.iSndBufSize = bs / co.bytesPerPkt(); } }; @@ -123,7 +123,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); // Mimimum recv buffer size is 32 packets - const int mssin_size = co.iMSS - CPacket::udpHeaderSize(AF_INET); + const int mssin_size = co.bytesPerPkt(); if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) co.iRcvBufSize = val / mssin_size; diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 72ae9808f..bd222b327 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -328,6 +328,10 @@ struct CSrtConfig: CSrtMuxerConfig int set(SRT_SOCKOPT optName, const void* val, int size); bool payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW; + + // This function returns the number of bytes that are allocated + // for a single packet in the sender and receiver buffer. + int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 784274f86..939f7f9d0 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -564,6 +564,7 @@ enum SRT_REJECT_REASON #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif + SRT_REJ_SETTINGS, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; diff --git a/srtcore/stats.h b/srtcore/stats.h index 2c0276665..14b0d131e 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -101,26 +101,25 @@ class BytesPackets: public BytesPacketsCount public: typedef BytesPacketsCount count_type; + // Set IPv4-based header size value as a fallback. This will be fixed upon connection. BytesPackets() - : m_pPayloadSizeLocation(NULL) + : m_iPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) {} public: - void bindPayloadSize(int* sizeloc) + void setupHeaderSize(int size) { - m_pPayloadSizeLocation = sizeloc; + m_iPacketHeaderSize = size; } uint64_t bytesWithHdr() const { - SRT_ASSERT(m_pPayloadSizeLocation != NULL); - size_t header_size = CPacket::ETH_MAX_MTU_SIZE - *m_pPayloadSizeLocation; - return m_bytes + m_packets * header_size; + return m_bytes + m_packets * m_iPacketHeaderSize; } private: - int* m_pPayloadSizeLocation; + int m_iPacketHeaderSize; }; template @@ -135,10 +134,10 @@ struct Metric total += val; } - void bindPayloadSize(int* loc) + void setupHeaderSize(int loc) { - trace.bindPayloadSize(loc); - total.bindPayloadSize(loc); + trace.setupHeaderSize(loc); + total.setupHeaderSize(loc); } void reset() @@ -167,14 +166,14 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - Sender(int* payloadsize_loc) + void setupHeaderSize(int hdr_size) { -#define BIND(var) var.bindPayloadSize(payloadsize_loc) - BIND(sent); - BIND(sentUnique); - BIND(sentRetrans); - BIND(dropped); -#undef BIND +#define SETHSIZE(var) var.setupHeaderSize(hdr_size) + SETHSIZE(sent); + SETHSIZE(sentUnique); + SETHSIZE(sentRetrans); + SETHSIZE(dropped); +#undef SETHSIZE } void reset() @@ -220,17 +219,17 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - Receiver(int* payloadsize_loc) + void setupHeaderSize(int hdr_size) { -#define BIND(var) var.bindPayloadSize(payloadsize_loc) - BIND(recvd); - BIND(recvdUnique); - BIND(recvdRetrans); - BIND(lost); - BIND(dropped); - BIND(recvdBelated); - BIND(undecrypted); -#undef BIND +#define SETHSIZE(var) var.setupHeaderSize(hdr_size) + SETHSIZE(recvd); + SETHSIZE(recvdUnique); + SETHSIZE(recvdRetrans); + SETHSIZE(lost); + SETHSIZE(dropped); + SETHSIZE(recvdBelated); + SETHSIZE(undecrypted); +#undef SETHSIZE } void reset() diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 4dd235e06..f1ffceb29 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -1,10 +1,13 @@ #include "gtest/gtest.h" #include +#include #include #include "srt.h" +#include "sync.h" #include "netinet_any.h" using srt::sockaddr_any; +using namespace srt::sync; class TestIPv6 : public ::testing::Test @@ -36,6 +39,7 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); + m_CallerStarted = false; } void TearDown() @@ -48,7 +52,29 @@ class TestIPv6 } public: + + void SetupFileMode() + { + int val = SRTT_FILE; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + ASSERT_NE(srt_setsockflag(m_listener_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + } + + int m_CallerPayloadSize = 0; + int m_AcceptedPayloadSize = 0; + atomic m_CallerStarted; + + Condition m_ReadyToClose; + Mutex m_ReadyToCloseLock; + + // "default parameter" version. Can't use default parameters because this goes + // against binding parameters. Nor overloading. void ClientThread(int family, const std::string& address) + { + return ClientThreadFlex(family, address, true); + } + + void ClientThreadFlex(int family, const std::string& address, bool shouldwork) { sockaddr_any sa (family); sa.hport(m_listen_port); @@ -56,12 +82,38 @@ class TestIPv6 std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + + m_CallerStarted = true; + const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); - EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); - if (connect_res == SRT_ERROR) - srt_close(m_listener_sock); - PrintAddresses(m_caller_sock, "CALLER"); + if (shouldwork) + { + // Version with expected success + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + + if (connect_res == SRT_ERROR) + { + srt_close(m_listener_sock); + } + else + { + before_closing.wait(); + } + + PrintAddresses(m_caller_sock, "CALLER"); + } + else + { + // Version with expected failure + EXPECT_EQ(connect_res, SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); + srt_close(m_listener_sock); + } } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -81,6 +133,8 @@ class TestIPv6 if (accepted_sock == SRT_INVALID_SOCK) { return sockaddr_any(); } + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); PrintAddresses(accepted_sock, "ACCEPTED"); @@ -95,6 +149,9 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + before_closing.notify_one(); + srt_close(accepted_sock); return sn; } @@ -194,3 +251,82 @@ TEST_F(TestIPv6, v6_calls_v4) client.join(); } +TEST_F(TestIPv6, plsize_v6) +{ + SetupFileMode(); + + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv6. + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0); + ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1444); // == 1500 - 32[IPv6] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1444); + + client.join(); +} + +TEST_F(TestIPv6, plsize_v4) +{ + SetupFileMode(); + + sockaddr_any sa (AF_INET); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv4. + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1456); // == 1500 - 20[IPv4] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1456); + + client.join(); +} + +TEST_F(TestIPv6, plsize_faux_v6) +{ + using namespace std::literals; + SetupFileMode(); + + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv6. + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0); + ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + int oversize = 1450; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &oversize, sizeof (int)), -1); + + std::thread client(&TestIPv6::ClientThreadFlex, this, AF_INET6, "::1", false); + + // Set on sleeping to make sure that the thread started. + // Sleeping isn't reliable so do a dampened spinlock here. + // This flag also confirms that the caller acquired the mutex and will + // unlock it for CV waiting - so we can proceed to notifying it. + do std::this_thread::sleep_for(100ms); while (!m_CallerStarted); + + // Just in case of a test failure, kick CV to avoid deadlock + CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + before_closing.notify_one(); + + client.join(); +} From 56fdfdb5f741423ff962ee48c4b5ea572be279ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 18:04:03 +0100 Subject: [PATCH 064/517] Withdrawn quotes for option names --- docs/API/API-socket-options.md | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 2730dcc92..68e22d7c8 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -264,7 +264,7 @@ The following table lists SRT API socket options in alphabetical order. Option d ### Option Descriptions -#### `SRTO_BINDTODEVICE` +#### SRTO_BINDTODEVICE | OptName | Since | Restrict | Type | Units | Default | Range | Dir |Entity| | --------------------- | ----- | -------- | -------- | ------ | -------- | ------ |-----|------| @@ -287,7 +287,7 @@ for a process that runs as root. Otherwise the function that applies the setting --- -#### `SRTO_CONGESTION` +#### SRTO_CONGESTION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -309,7 +309,7 @@ rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANST --- -#### `SRTO_CONNTIMEO` +#### SRTO_CONNTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -323,7 +323,7 @@ will be 10 times the value set with `SRTO_CONNTIMEO`. --- -#### `SRTO_CRYPTOMODE` +#### SRTO_CRYPTOMODE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ------------------ | --------- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -377,7 +377,7 @@ There is no way to check the crypto mode being requested by the SRT caller at th --- -#### `SRTO_DRIFTTRACER` +#### SRTO_DRIFTTRACER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -389,7 +389,7 @@ Enables or disables time drift tracer (receiver). --- -#### `SRTO_ENFORCEDENCRYPTION` +#### SRTO_ENFORCEDENCRYPTION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -434,7 +434,7 @@ on the caller side. --- -#### `SRTO_EVENT` +#### SRTO_EVENT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -449,7 +449,7 @@ Possible values are those defined in `SRT_EPOLL_OPT` enum (a combination of --- -#### `SRTO_FC` +#### SRTO_FC | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -488,7 +488,7 @@ where `latency_sec` is the receiver buffering delay ([SRTO_RCVLATENCY](#SRTO_RCV --- -#### `SRTO_GROUPCONNECT` +#### SRTO_GROUPCONNECT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | --------- | ------ | -------- | ------ | --- | ------ | @@ -514,7 +514,7 @@ function will return the group, not this socket ID. --- -#### `SRTO_GROUPMINSTABLETIMEO` +#### SRTO_GROUPMINSTABLETIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -548,7 +548,7 @@ Note that the value of this option is not allowed to exceed the value of --- -#### `SRTO_GROUPTYPE` +#### SRTO_GROUPTYPE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -567,7 +567,7 @@ context than inside the listener callback handler, the value is undefined. --- -#### `SRTO_INPUTBW` +#### SRTO_INPUTBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -588,7 +588,7 @@ and keep the default 25% value for `SRTO_OHEADBW`*. --- -#### `SRTO_MININPUTBW` +#### SRTO_MININPUTBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -603,7 +603,7 @@ See [`SRTO_INPUTBW`](#SRTO_INPUTBW). --- -#### `SRTO_IPTOS` +#### SRTO_IPTOS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -621,7 +621,7 @@ and the actual value for connected sockets. --- -#### `SRTO_IPTTL` +#### SRTO_IPTTL | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -639,7 +639,7 @@ and the actual value for connected sockets. --- -#### `SRTO_IPV6ONLY` +#### SRTO_IPV6ONLY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -661,7 +661,7 @@ reliable way). Possible values are: --- -#### `SRTO_ISN` +#### SRTO_ISN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ---------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -678,7 +678,7 @@ used in any regular development.* --- -#### `SRTO_KMPREANNOUNCE` +#### SRTO_KMPREANNOUNCE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ----------------- | ------ | --- | ------ | @@ -712,7 +712,7 @@ The value of `SRTO_KMPREANNOUNCE must not exceed `(SRTO_KMREFRESHRATE - 1) / 2`. --- -#### `SRTO_KMREFRESHRATE` +#### SRTO_KMREFRESHRATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | ---------------- | ------ | --- | ------ | @@ -734,7 +734,7 @@ might still be in flight, or packets that have to be retransmitted. --- -#### `SRTO_KMSTATE` +#### SRTO_KMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -752,7 +752,7 @@ for more details. --- -#### `SRTO_LATENCY` +#### SRTO_LATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -771,7 +771,7 @@ be sender and receiver at the same time, and `SRTO_SENDER` became redundant. --- -#### `SRTO_LINGER` +#### SRTO_LINGER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------ | -------- | ------ | --- | ------ | @@ -789,7 +789,7 @@ The default value in [the file transfer configuration](./API.md#transmission-typ --- -#### `SRTO_LOSSMAXTTL` +#### SRTO_LOSSMAXTTL | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -810,7 +810,7 @@ By default this value is set to 0, which means that this mechanism is off. --- -#### `SRTO_MAXBW` +#### SRTO_MAXBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -834,7 +834,7 @@ therefore the default -1 remains even in live mode. --- -#### `SRTO_MESSAGEAPI` +#### SRTO_MESSAGEAPI | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -873,7 +873,7 @@ SCTP protocol. --- -#### `SRTO_MINVERSION` +#### SRTO_MINVERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -889,7 +889,7 @@ The default value is 0x010000 (SRT v1.0.0). --- -#### `SRTO_MSS` +#### SRTO_MSS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -973,7 +973,7 @@ the best approach is then to avoid it by using appropriate parameters. --- -#### `SRTO_NAKREPORT` +#### SRTO_NAKREPORT | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -990,7 +990,7 @@ The default is true for Live mode, and false for File mode (see [`SRTO_TRANSTYPE --- -#### `SRTO_OHEADBW` +#### SRTO_OHEADBW | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1020,7 +1020,7 @@ and break quickly at any rise in packet loss. --- -#### `SRTO_PACKETFILTER` +#### SRTO_PACKETFILTER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1086,7 +1086,7 @@ For details, see [SRT Packet Filtering & FEC](../features/packet-filtering-and-f --- -#### `SRTO_PASSPHRASE` +#### SRTO_PASSPHRASE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1115,7 +1115,7 @@ encrypted connection, they have to simply set the same passphrase. --- -#### `SRTO_PAYLOADSIZE` +#### SRTO_PAYLOADSIZE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1173,7 +1173,7 @@ For File mode: Default value is 0 and it's recommended not to be changed. --- -#### `SRTO_PBKEYLEN` +#### SRTO_PBKEYLEN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1260,7 +1260,7 @@ undefined behavior: --- -#### `SRTO_PEERIDLETIMEO` +#### SRTO_PEERIDLETIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1274,7 +1274,7 @@ considered broken on timeout. --- -#### `SRTO_PEERLATENCY` +#### SRTO_PEERLATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1295,7 +1295,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### `SRTO_PEERVERSION` +#### SRTO_PEERVERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | @@ -1309,7 +1309,7 @@ See [`SRTO_VERSION`](#SRTO_VERSION) for the version format. --- -#### `SRTO_RCVBUF` +#### SRTO_RCVBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1329,7 +1329,7 @@ than the Flight Flag size). --- -#### `SRTO_RCVDATA` +#### SRTO_RCVDATA | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1341,7 +1341,7 @@ Size of the available data in the receive buffer. --- -#### `SRTO_RCVKMSTATE` +#### SRTO_RCVKMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1355,7 +1355,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### `SRTO_RCVLATENCY` +#### SRTO_RCVLATENCY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1397,7 +1397,7 @@ See also [`SRTO_LATENCY`](#SRTO_LATENCY). --- -#### `SRTO_RCVSYN` +#### SRTO_RCVSYN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1434,7 +1434,7 @@ derived from the socket of which the group is a member). --- -#### `SRTO_RCVTIMEO` +#### SRTO_RCVTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1448,7 +1448,7 @@ it will behave as if in "non-blocking mode". The -1 value means no time limit. --- -#### `SRTO_RENDEZVOUS` +#### SRTO_RENDEZVOUS | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1461,7 +1461,7 @@ procedure of `srt_bind` and then `srt_connect` (or `srt_rendezvous`) to one anot --- -#### `SRTO_RETRANSMITALGO` +#### SRTO_RETRANSMITALGO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | --------------------- | ----- | -------- | --------- | ------ | ------- | ------ | --- | ------ | @@ -1489,7 +1489,7 @@ Periodic NAK reports. See [SRTO_NAKREPORT](#SRTO_NAKREPORT). --- -#### `SRTO_REUSEADDR` +#### SRTO_REUSEADDR | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1516,7 +1516,7 @@ its address.* --- -#### `SRTO_SENDER` +#### SRTO_SENDER | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | ---------- | ------ | --- | ------ | @@ -1536,7 +1536,7 @@ parties simultaneously. --- -#### `SRTO_SNDBUF` +#### SRTO_SNDBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1548,7 +1548,7 @@ Sender Buffer Size. See [`SRTO_RCVBUF`](#SRTO_RCVBUF) for more information. --- -#### `SRTO_SNDDATA` +#### SRTO_SNDDATA | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1560,7 +1560,7 @@ Size of the unacknowledged data in send buffer. --- -#### `SRTO_SNDDROPDELAY` +#### SRTO_SNDDROPDELAY | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1587,7 +1587,7 @@ always when requested). --- -#### `SRTO_SNDKMSTATE` +#### SRTO_SNDKMSTATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1601,7 +1601,7 @@ Values defined in enum [`SRT_KM_STATE`](#srt_km_state). --- -#### `SRTO_SNDSYN` +#### SRTO_SNDSYN | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1627,7 +1627,7 @@ but will have no effect on the listener socket itself. --- -#### `SRTO_SNDTIMEO` +#### SRTO_SNDTIMEO | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1641,7 +1641,7 @@ if in "non-blocking mode". The -1 value means no time limit. --- -#### `SRTO_STATE` +#### SRTO_STATE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1653,7 +1653,7 @@ Returns the current socket state, same as `srt_getsockstate`. --- -#### `SRTO_STREAMID` +#### SRTO_STREAMID | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1682,7 +1682,7 @@ influence anything. --- -#### `SRTO_TLPKTDROP` +#### SRTO_TLPKTDROP | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1701,7 +1701,7 @@ enabled in sender if receiver supports it. --- -#### `SRTO_TRANSTYPE` +#### SRTO_TRANSTYPE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1717,7 +1717,7 @@ Values defined by enum `SRT_TRANSTYPE` (see above for possible values) --- -#### `SRTO_TSBPDMODE` +#### SRTO_TSBPDMODE | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1735,7 +1735,7 @@ the application. --- -#### `SRTO_UDP_RCVBUF` +#### SRTO_UDP_RCVBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1748,7 +1748,7 @@ based on MSS value. Receive buffer must not be greater than FC size. --- -#### `SRTO_UDP_SNDBUF` +#### SRTO_UDP_SNDBUF | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | @@ -1761,7 +1761,7 @@ on `SRTO_MSS` value. --- -#### `SRTO_VERSION` +#### SRTO_VERSION | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | ----------------- | ----- | -------- | ---------- | ------- | --------- | ------ | --- | ------ | From d7f32d067bb0d1e15a805a72d3060cf161956729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 3 Mar 2023 18:07:04 +0100 Subject: [PATCH 065/517] Fixed links in the socket option table --- docs/API/API-socket-options.md | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 68e22d7c8..6a23aa349 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -201,66 +201,66 @@ The following table lists SRT API socket options in alphabetical order. Option d | Option Name | Since | Restrict | Type | Units | Default | Range | Dir |Entity | | :------------------------------------------------------ | :---: | :------: | :-------: | :-----: | :---------------: | :------: |:---:|:-----:| -| [`SRTO_BINDTODEVICE`](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | -| [`SRTO_CONGESTION`](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | \* | W | S | -| [`SRTO_CONNTIMEO`](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | -| [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE) | 1.6.0-dev | pre | `int32_t` | | 0 (Auto) | [0, 2] | W | GSD | -| [`SRTO_DRIFTTRACER`](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | -| [`SRTO_ENFORCEDENCRYPTION`](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | -| [`SRTO_EVENT`](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | -| [`SRTO_FC`](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | -| [`SRTO_GROUPCONNECT`](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | -| [`SRTO_GROUPMINSTABLETIMEO`](#SRTO_GROUPMINSTABLETIMEO) | 1.5.0 | pre | `int32_t` | ms | 60 | 60-... | W | GDI+ | -| [`SRTO_GROUPTYPE`](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_INPUTBW`](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_IPTOS`](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | -| [`SRTO_IPTTL`](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | -| [`SRTO_IPV6ONLY`](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | -| [`SRTO_ISN`](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | -| [`SRTO_KMPREANNOUNCE`](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. \* | RW | GSD | -| [`SRTO_KMREFRESHRATE`](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | -| [`SRTO_KMSTATE`](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | -| [`SRTO_LATENCY`](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 \* | 0.. | RW | GSD | -| [`SRTO_LINGER`](#SRTO_LINGER) | | post | `linger` | s | off \* | 0.. | RW | GSD | -| [`SRTO_LOSSMAXTTL`](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | -| [`SRTO_MAXBW`](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | -| [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | -| [`SRTO_MININPUTBW`](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | -| [`SRTO_MINVERSION`](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | \* | RW | GSD | -| [`SRTO_MSS`](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | -| [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | \* | | RW | GSD+ | -| [`SRTO_OHEADBW`](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | -| [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_PASSPHRASE`](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..80] | W | GSD | -| [`SRTO_PAYLOADSIZE`](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | -| [`SRTO_PBKEYLEN`](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | \* | RW | GSD | -| [`SRTO_PEERIDLETIMEO`](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | -| [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | -| [`SRTO_PEERVERSION`](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | -| [`SRTO_RCVBUF`](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | -| [`SRTO_RCVDATA`](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_RCVKMSTATE`](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | \* | 0.. | RW | GSD | -| [`SRTO_RCVSYN`](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_RCVTIMEO`](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | -| [`SRTO_RENDEZVOUS`](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | -| [`SRTO_RETRANSMITALGO`](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | -| [`SRTO_REUSEADDR`](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | -| [`SRTO_SENDER`](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | -| [`SRTO_SNDBUF`](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | -| [`SRTO_SNDDATA`](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | -| [`SRTO_SNDDROPDELAY`](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | \* | -1.. | W | GSD+ | -| [`SRTO_SNDKMSTATE`](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | -| [`SRTO_SNDSYN`](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | -| [`SRTO_SNDTIMEO`](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | -| [`SRTO_STATE`](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | -| [`SRTO_STREAMID`](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | -| [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | \* | | RW | GSD | -| [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | \* | W | S | -| [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | \* | | W | S | -| [`SRTO_UDP_RCVBUF`](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | -| [`SRTO_UDP_SNDBUF`](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | \* | RW | GSD+ | -| [`SRTO_VERSION`](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | +| [SRTO_BINDTODEVICE](#SRTO_BINDTODEVICE) | 1.4.2 | pre-bind | `string` | | | | RW | GSD+ | +| [SRTO_CONGESTION](#SRTO_CONGESTION) | 1.3.0 | pre | `string` | | "live" | \* | W | S | +| [SRTO_CONNTIMEO](#SRTO_CONNTIMEO) | 1.1.2 | pre | `int32_t` | ms | 3000 | 0.. | W | GSD+ | +| [SRTO_CRYPTOMODE](#SRTO_CRYPTOMODE) | 1.6.0-dev | pre | `int32_t` | | 0 (Auto) | [0, 2] | W | GSD | +| [SRTO_DRIFTTRACER](#SRTO_DRIFTTRACER) | 1.4.2 | post | `bool` | | true | | RW | GSD | +| [SRTO_ENFORCEDENCRYPTION](#SRTO_ENFORCEDENCRYPTION) | 1.3.2 | pre | `bool` | | true | | W | GSD | +| [SRTO_EVENT](#SRTO_EVENT) | | | `int32_t` | flags | | | R | S | +| [SRTO_FC](#SRTO_FC) | | pre | `int32_t` | pkts | 25600 | 32.. | RW | GSD | +| [SRTO_GROUPCONNECT](#SRTO_GROUPCONNECT) | 1.5.0 | pre | `int32_t` | | 0 | 0...1 | W | S | +| [SRTO_GROUPMINSTABLETIMEO](#SRTO_GROUPMINSTABLETIMEO) | 1.5.0 | pre | `int32_t` | ms | 60 | 60-... | W | GDI+ | +| [SRTO_GROUPTYPE](#SRTO_GROUPTYPE) | 1.5.0 | | `int32_t` | enum | | | R | S | +| [SRTO_INPUTBW](#SRTO_INPUTBW) | 1.0.5 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [SRTO_IPTOS](#SRTO_IPTOS) | 1.0.5 | pre-bind | `int32_t` | | (system) | 0..255 | RW | GSD | +| [SRTO_IPTTL](#SRTO_IPTTL) | 1.0.5 | pre-bind | `int32_t` | hops | (system) | 1..255 | RW | GSD | +| [SRTO_IPV6ONLY](#SRTO_IPV6ONLY) | 1.4.0 | pre-bind | `int32_t` | | (system) | -1..1 | RW | GSD | +| [SRTO_ISN](#SRTO_ISN) | 1.3.0 | | `int32_t` | | | | R | S | +| [SRTO_KMPREANNOUNCE](#SRTO_KMPREANNOUNCE) | 1.3.2 | pre | `int32_t` | pkts | 0: 212 | 0.. \* | RW | GSD | +| [SRTO_KMREFRESHRATE](#SRTO_KMREFRESHRATE) | 1.3.2 | pre | `int32_t` | pkts | 0: 224 | 0.. | RW | GSD | +| [SRTO_KMSTATE](#SRTO_KMSTATE) | 1.0.2 | | `int32_t` | enum | | | R | S | +| [SRTO_LATENCY](#SRTO_LATENCY) | 1.0.2 | pre | `int32_t` | ms | 120 \* | 0.. | RW | GSD | +| [SRTO_LINGER](#SRTO_LINGER) | | post | `linger` | s | off \* | 0.. | RW | GSD | +| [SRTO_LOSSMAXTTL](#SRTO_LOSSMAXTTL) | 1.2.0 | post | `int32_t` | packets | 0 | 0.. | RW | GSD+ | +| [SRTO_MAXBW](#SRTO_MAXBW) | | post | `int64_t` | B/s | -1 | -1.. | RW | GSD | +| [SRTO_MESSAGEAPI](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | +| [SRTO_MININPUTBW](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | +| [SRTO_MINVERSION](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | \* | RW | GSD | +| [SRTO_MSS](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [SRTO_NAKREPORT](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | \* | | RW | GSD+ | +| [SRTO_OHEADBW](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | +| [SRTO_PACKETFILTER](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | +| [SRTO_PASSPHRASE](#SRTO_PASSPHRASE) | 0.0.0 | pre | `string` | | "" | [10..80] | W | GSD | +| [SRTO_PAYLOADSIZE](#SRTO_PAYLOADSIZE) | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | +| [SRTO_PBKEYLEN](#SRTO_PBKEYLEN) | 0.0.0 | pre | `int32_t` | bytes | 0 | \* | RW | GSD | +| [SRTO_PEERIDLETIMEO](#SRTO_PEERIDLETIMEO) | 1.3.3 | pre | `int32_t` | ms | 5000 | 0.. | RW | GSD+ | +| [SRTO_PEERLATENCY](#SRTO_PEERLATENCY) | 1.3.0 | pre | `int32_t` | ms | 0 | 0.. | RW | GSD | +| [SRTO_PEERVERSION](#SRTO_PEERVERSION) | 1.1.0 | | `int32_t` | * | | | R | GS | +| [SRTO_RCVBUF](#SRTO_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | +| [SRTO_RCVDATA](#SRTO_RCVDATA) | | | `int32_t` | pkts | | | R | S | +| [SRTO_RCVKMSTATE](#SRTO_RCVKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [SRTO_RCVLATENCY](#SRTO_RCVLATENCY) | 1.3.0 | pre | `int32_t` | msec | \* | 0.. | RW | GSD | +| [SRTO_RCVSYN](#SRTO_RCVSYN) | | post | `bool` | | true | | RW | GSI | +| [SRTO_RCVTIMEO](#SRTO_RCVTIMEO) | | post | `int32_t` | ms | -1 | -1, 0.. | RW | GSI | +| [SRTO_RENDEZVOUS](#SRTO_RENDEZVOUS) | | pre | `bool` | | false | | RW | S | +| [SRTO_RETRANSMITALGO](#SRTO_RETRANSMITALGO) | 1.4.2 | pre | `int32_t` | | 1 | [0, 1] | RW | GSD | +| [SRTO_REUSEADDR](#SRTO_REUSEADDR) | | pre-bind | `bool` | | true | | RW | GSD | +| [SRTO_SENDER](#SRTO_SENDER) | 1.0.4 | pre | `bool` | | false | | W | S | +| [SRTO_SNDBUF](#SRTO_SNDBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | +| [SRTO_SNDDATA](#SRTO_SNDDATA) | | | `int32_t` | pkts | | | R | S | +| [SRTO_SNDDROPDELAY](#SRTO_SNDDROPDELAY) | 1.3.2 | post | `int32_t` | ms | \* | -1.. | W | GSD+ | +| [SRTO_SNDKMSTATE](#SRTO_SNDKMSTATE) | 1.2.0 | | `int32_t` | enum | | | R | S | +| [SRTO_SNDSYN](#SRTO_SNDSYN) | | post | `bool` | | true | | RW | GSI | +| [SRTO_SNDTIMEO](#SRTO_SNDTIMEO) | | post | `int32_t` | ms | -1 | -1.. | RW | GSI | +| [SRTO_STATE](#SRTO_STATE) | | | `int32_t` | enum | | | R | S | +| [SRTO_STREAMID](#SRTO_STREAMID) | 1.3.0 | pre | `string` | | "" | [512] | RW | GSD | +| [SRTO_TLPKTDROP](#SRTO_TLPKTDROP) | 1.0.6 | pre | `bool` | | \* | | RW | GSD | +| [SRTO_TRANSTYPE](#SRTO_TRANSTYPE) | 1.3.0 | pre | `int32_t` | enum |`SRTT_LIVE` | \* | W | S | +| [SRTO_TSBPDMODE](#SRTO_TSBPDMODE) | 0.0.0 | pre | `bool` | | \* | | W | S | +| [SRTO_UDP_RCVBUF](#SRTO_UDP_RCVBUF) | | pre-bind | `int32_t` | bytes | 8192 payloads | \* | RW | GSD+ | +| [SRTO_UDP_SNDBUF](#SRTO_UDP_SNDBUF) | | pre-bind | `int32_t` | bytes | 65536 | \* | RW | GSD+ | +| [SRTO_VERSION](#SRTO_VERSION) | 1.1.0 | | `int32_t` | | | | R | S | ### Option Descriptions From 70de76348d31adb8bbe572eda1c2d7d24a35eea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 14:43:01 +0100 Subject: [PATCH 066/517] Fixed minimum MSS to 116. Fixed some other bux --- docs/API/API-socket-options.md | 4 ++-- srtcore/socketconfig.cpp | 8 +++++++- srtcore/stats.h | 8 ++++---- test/test_file_transmission.cpp | 6 ------ test/test_socket_options.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 6a23aa349..ef83cf220 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -227,7 +227,7 @@ The following table lists SRT API socket options in alphabetical order. Option d | [SRTO_MESSAGEAPI](#SRTO_MESSAGEAPI) | 1.3.0 | pre | `bool` | | true | | W | GSD | | [SRTO_MININPUTBW](#SRTO_MININPUTBW) | 1.4.3 | post | `int64_t` | B/s | 0 | 0.. | RW | GSD | | [SRTO_MINVERSION](#SRTO_MINVERSION) | 1.3.0 | pre | `int32_t` | version | 0x010000 | \* | RW | GSD | -| [SRTO_MSS](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| [SRTO_MSS](#SRTO_MSS) | | pre-bind | `int32_t` | bytes | 1500 | 116.. | RW | GSD | | [SRTO_NAKREPORT](#SRTO_NAKREPORT) | 1.1.0 | pre | `bool` | | \* | | RW | GSD+ | | [SRTO_OHEADBW](#SRTO_OHEADBW) | 1.0.5 | post | `int32_t` | % | 25 | 5..100 | RW | GSD | | [SRTO_PACKETFILTER](#SRTO_PACKETFILTER) | 1.4.0 | pre | `string` | | "" | [512] | RW | GSD | @@ -893,7 +893,7 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 116.. | RW | GSD | Maximum Segment Size. This value represents the maximum size of a UDP packet sent by the system. Therefore the value of `SRTO_MSS` must not exceed the diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 27eda173b..8ea2e19bb 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -69,9 +69,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + using namespace srt_logging; const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::udpHeaderSize(AF_INET6) + CHandShake::m_iContentSize)) + const int handshake_size = CHandShake::m_iContentSize + (sizeof(uint32_t) * SRT_HS_E_SIZE); + const int minval = int(CPacket::udpHeaderSize(AF_INET6) + CPacket::HDR_SIZE + handshake_size); + if (ival < minval) + { + LOGC(kmlog.Error, log << "SRTO_MSS: minimum value allowed is " << minval << " = [IPv6][UDP][SRT] headers + minimum SRT handshake"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } co.iMSS = ival; diff --git a/srtcore/stats.h b/srtcore/stats.h index 14b0d131e..55d8d00d9 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -103,23 +103,23 @@ class BytesPackets: public BytesPacketsCount // Set IPv4-based header size value as a fallback. This will be fixed upon connection. BytesPackets() - : m_iPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) + : m_zPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) {} public: void setupHeaderSize(int size) { - m_iPacketHeaderSize = size; + m_zPacketHeaderSize = uint64_t(size); } uint64_t bytesWithHdr() const { - return m_bytes + m_packets * m_iPacketHeaderSize; + return m_bytes + m_packets * m_zPacketHeaderSize; } private: - int m_iPacketHeaderSize; + uint64_t m_zPacketHeaderSize; }; template diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 48d790471..50db5647a 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -226,8 +226,6 @@ TEST(FileTransmission, Setup46) int ipv4_and_ipv6 = 0; ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); - // srt_setloglevel(LOG_DEBUG); - ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); int connect_port = 5555; @@ -238,10 +236,6 @@ TEST(FileTransmission, Setup46) sa_lsn.sin_addr.s_addr = INADDR_ANY; sa_lsn.sin_port = htons(connect_port); - - srt_setloglevel(LOG_DEBUG); - - // Find unused a port not used by any other service. // Otherwise srt_connect may actually connect. int bind_res = -1; diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index 9b6385344..f010d3aeb 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -193,7 +193,7 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, //SRTO_MININPUTBW { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, - { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, + { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 116, 65536, 1500, 1400, {-1, 0, 75} }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101} }, //SRTO_PACKETFILTER From 2eb1159ae2116e82855951e7984df87db460d969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:21:41 +0100 Subject: [PATCH 067/517] Replaced rand_r with std c++ random --- test/test_file_transmission.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 50db5647a..de227cce3 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -268,10 +268,14 @@ TEST(FileTransmission, Setup46) const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 char buffer[SIZE]; - unsigned int randseed = std::time(NULL); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); for (size_t i = 0; i < SIZE; ++i) - buffer[i] = rand_r(&randseed); + { + buffer[i] = dis(mtrd); + } EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); From 9f48d3ca05cde662e5ebf0e0c067f14567f19ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:32:39 +0100 Subject: [PATCH 068/517] Fixed usage of C++14 literals in the test (build failures) --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index f1ffceb29..a889851e5 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -300,7 +300,7 @@ TEST_F(TestIPv6, plsize_v4) TEST_F(TestIPv6, plsize_faux_v6) { - using namespace std::literals; + using namespace std::chrono; SetupFileMode(); sockaddr_any sa (AF_INET6); @@ -322,7 +322,7 @@ TEST_F(TestIPv6, plsize_faux_v6) // Sleeping isn't reliable so do a dampened spinlock here. // This flag also confirms that the caller acquired the mutex and will // unlock it for CV waiting - so we can proceed to notifying it. - do std::this_thread::sleep_for(100ms); while (!m_CallerStarted); + do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); // Just in case of a test failure, kick CV to avoid deadlock CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); From edbb608a98400a10a456ebd00f160c34ee4b8940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 15:46:18 +0100 Subject: [PATCH 069/517] [MAINT] Upgraded CI: ubuntu to version 20.04 --- .github/workflows/android.yaml | 2 +- .github/workflows/cxx11-ubuntu.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 713c3f331..0af85fda3 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: NDK-R23 - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Setup Android NDK R23 diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 751b3fda1..500ff1beb 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -9,7 +9,7 @@ on: jobs: build: name: ubuntu - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 From 26a7be6e45b452e0f238d9b195c054a6bfc76302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 16:18:07 +0100 Subject: [PATCH 070/517] Fixed test logics (printing after closing) --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index a889851e5..e1c5844c2 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -96,6 +96,8 @@ class TestIPv6 int size = sizeof (int); EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + PrintAddresses(m_caller_sock, "CALLER"); + if (connect_res == SRT_ERROR) { srt_close(m_listener_sock); @@ -104,8 +106,6 @@ class TestIPv6 { before_closing.wait(); } - - PrintAddresses(m_caller_sock, "CALLER"); } else { From 3d387a2930784fa3fe95f46bf536cfd80c3a7925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 16:49:58 +0100 Subject: [PATCH 071/517] Attempted fix for a deadlock in test, added some tracking --- test/test_ipv6.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e1c5844c2..6498b6551 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -80,7 +80,7 @@ class TestIPv6 sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); @@ -100,10 +100,13 @@ class TestIPv6 if (connect_res == SRT_ERROR) { + std::cout << "Connect failed - [UNLOCK]\n"; + before_closing.locker().unlock(); // We don't need this lock here and it may deadlock srt_close(m_listener_sock); } else { + std::cout << "Connect succeeded, [UNLOCK-WAIT-CV]\n"; before_closing.wait(); } } @@ -149,6 +152,7 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + std::cout << "DoAccept: [LOCK-SIGNAL]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); before_closing.notify_one(); From 3eef5922a61cd50b8888f22f74bfeb3356008993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 17:16:50 +0100 Subject: [PATCH 072/517] Added expect and tracking to close socket in ReuseAddr test (Travis problem) --- test/test_reuseaddr.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index 60532eb80..3fb2168cd 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -404,14 +404,15 @@ void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_succes char pattern[4] = {1, 2, 3, 4}; - ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), + EXPECT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), 1316); EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << " LSN:@" << bindsock << " CLR:@" << g_client_sock << " ...\n"; - ASSERT_NE(srt_close(accepted_sock), SRT_ERROR); - ASSERT_NE(srt_close(g_client_sock), SRT_ERROR); // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + EXPECT_NE(srt_close(accepted_sock), SRT_ERROR) << "ERROR: " << srt_getlasterror_str(); + // cannot close g_client_sock after srt_sendmsg because of issue in api.c:2346 + EXPECT_NE(srt_close(g_client_sock), SRT_ERROR) << "ERROR: " << srt_getlasterror_str(); std::cout << "[T/S] joining client async...\n"; launched.get(); From e2ab5f61b5a6c1159d4b2c85920dee5815e2a36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 6 Mar 2023 18:16:47 +0100 Subject: [PATCH 073/517] Used relaxed signaling for the sake of Travis --- test/test_ipv6.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 6498b6551..472876ba8 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -153,8 +153,8 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - before_closing.notify_one(); + // XXX Deadlock here on Travis by unknown reason, hence do relaxed signaling. + CSync::notify_all_relaxed(m_ReadyToClose); srt_close(accepted_sock); return sn; From d081e507aa732e1b4f8221e99db6012ff161c740 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 12:23:02 +0100 Subject: [PATCH 074/517] Lock debug fix for tests --- test/test_ipv6.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 472876ba8..959ace41b 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -80,10 +80,12 @@ class TestIPv6 sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK]\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); + std::cout << "[LOCKED] Connecting\n"; + m_CallerStarted = true; const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); @@ -106,8 +108,9 @@ class TestIPv6 } else { - std::cout << "Connect succeeded, [UNLOCK-WAIT-CV]\n"; + std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; before_closing.wait(); + std::cout << "Connect: [SIGNALED-LOCK]\n"; } } else @@ -117,6 +120,7 @@ class TestIPv6 EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); srt_close(m_listener_sock); } + std::cout << "Connect: [UNLOCKING...]\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -124,7 +128,10 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; - std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + // Printing may happen from different threads, avoid intelining. + std::ostringstream sout; + sout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + std::cout << sout.str(); } sockaddr_any DoAccept() @@ -153,8 +160,8 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - // XXX Deadlock here on Travis by unknown reason, hence do relaxed signaling. - CSync::notify_all_relaxed(m_ReadyToClose); + CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); + std::cout << "DoAccept: [UNLOCKED]\n"; srt_close(accepted_sock); return sn; @@ -329,8 +336,9 @@ TEST_F(TestIPv6, plsize_faux_v6) do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); // Just in case of a test failure, kick CV to avoid deadlock - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - before_closing.notify_one(); + std::cout << "TEST: [LOCK-SIGNAL]\n"; + CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); + std::cout << "TEST: [UNLOCKED]\n"; client.join(); } From ba5f9620118a5754b71a92a6e4ae29661cd7c203 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 12:53:03 +0100 Subject: [PATCH 075/517] Added timeout for lock-CV to avoid Travis problem --- test/test_ipv6.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 959ace41b..531f11526 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -109,8 +109,8 @@ class TestIPv6 else { std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; - before_closing.wait(); - std::cout << "Connect: [SIGNALED-LOCK]\n"; + bool signaled = before_closing.wait_for(seconds_from(10)); + std::cout << "Connect: [" << (signaled ? "SIGNALED" : "EXPIRED") << "-LOCK]\n"; } } else @@ -138,6 +138,8 @@ class TestIPv6 { sockaddr_any sc1; + using namespace std::chrono; + SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); if (accepted_sock == SRT_INVALID_SOCK) { @@ -160,8 +162,23 @@ class TestIPv6 } std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); - std::cout << "DoAccept: [UNLOCKED]\n"; + + // Travis makes problems here by unknown reason. Try waiting up to 10s + // until it's possible, otherwise simply give up. The intention is to + // prevent from closing too soon before the caller thread has a chance + // to perform required verifications. After 10s we can consider it enough time. + + int nms = 10; + while (!m_ReadyToCloseLock.try_lock()) + { + this_thread::sleep_for(milliseconds_from(100)); + if (--nms == 0) + break; + } + + CSync::notify_all_relaxed(m_ReadyToClose); + m_ReadyToCloseLock.unlock(); + std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; srt_close(accepted_sock); return sn; From 320a79b8087442478bb268ad58565e72e14c6cd0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 14:07:40 +0100 Subject: [PATCH 076/517] Attempted more debug for test ipv6 for Travis --- test/test_ipv6.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 531f11526..70dfcd5dc 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -174,10 +174,14 @@ class TestIPv6 this_thread::sleep_for(milliseconds_from(100)); if (--nms == 0) break; + std::cout << "(lock failed, retrying " << nms << ")...\n"; } CSync::notify_all_relaxed(m_ReadyToClose); - m_ReadyToCloseLock.unlock(); + if (nms) + { + m_ReadyToCloseLock.unlock(); + } std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; srt_close(accepted_sock); From 1b3ccd78563935a69ffc8fc3d3f7863646470376 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 7 Mar 2023 14:23:23 +0100 Subject: [PATCH 077/517] More debug for Travis --- test/test_ipv6.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 70dfcd5dc..e103c2a51 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -172,14 +172,16 @@ class TestIPv6 while (!m_ReadyToCloseLock.try_lock()) { this_thread::sleep_for(milliseconds_from(100)); + std::cout << "(lock failed, retrying " << nms << ")...\n"; if (--nms == 0) break; - std::cout << "(lock failed, retrying " << nms << ")...\n"; } + std::cout << "(signaling...)\n"; CSync::notify_all_relaxed(m_ReadyToClose); if (nms) { + std::cout << "(unlocking...)\n"; m_ReadyToCloseLock.unlock(); } std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; From 90b13b1175492454ec86f10c306d2409ac372da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 7 Mar 2023 16:45:51 +0100 Subject: [PATCH 078/517] Fixed test ipv6 to use promise-future for synchronization --- test/test_ipv6.cpp | 73 +++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e103c2a51..4a464f1c4 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "srt.h" #include "sync.h" #include "netinet_any.h" @@ -39,7 +40,11 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); - m_CallerStarted = false; + + m_CallerStarted.reset(new std::promise); + m_ReadyCaller.reset(new std::promise); + m_ReadyAccept.reset(new std::promise); + } void TearDown() @@ -62,10 +67,8 @@ class TestIPv6 int m_CallerPayloadSize = 0; int m_AcceptedPayloadSize = 0; - atomic m_CallerStarted; - Condition m_ReadyToClose; - Mutex m_ReadyToCloseLock; + std::unique_ptr> m_CallerStarted, m_ReadyCaller, m_ReadyAccept; // "default parameter" version. Can't use default parameters because this goes // against binding parameters. Nor overloading. @@ -76,17 +79,15 @@ class TestIPv6 void ClientThreadFlex(int family, const std::string& address, bool shouldwork) { + std::future ready_accepter = m_ReadyAccept->get_future(); + sockaddr_any sa (family); sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; - CUniqueSync before_closing(m_ReadyToCloseLock, m_ReadyToClose); - - std::cout << "[LOCKED] Connecting\n"; - - m_CallerStarted = true; + m_CallerStarted->set_value(); const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); @@ -98,19 +99,19 @@ class TestIPv6 int size = sizeof (int); EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + m_ReadyCaller->set_value(); + PrintAddresses(m_caller_sock, "CALLER"); if (connect_res == SRT_ERROR) { std::cout << "Connect failed - [UNLOCK]\n"; - before_closing.locker().unlock(); // We don't need this lock here and it may deadlock srt_close(m_listener_sock); } else { - std::cout << "Connect succeeded, [UNLOCK-WAIT-CV...]\n"; - bool signaled = before_closing.wait_for(seconds_from(10)); - std::cout << "Connect: [" << (signaled ? "SIGNALED" : "EXPIRED") << "-LOCK]\n"; + std::cout << "Connect succeeded, [FUTURE-WAIT...]\n"; + ready_accepter.wait(); } } else @@ -120,7 +121,7 @@ class TestIPv6 EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); srt_close(m_listener_sock); } - std::cout << "Connect: [UNLOCKING...]\n"; + std::cout << "Connect: exit\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -138,21 +139,24 @@ class TestIPv6 { sockaddr_any sc1; - using namespace std::chrono; + // Make sure the caller started + m_CallerStarted->get_future().wait(); + std::cout << "DoAccept: caller started, proceeding to accept\n"; SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); if (accepted_sock == SRT_INVALID_SOCK) { return sockaddr_any(); } - int size = sizeof (int); - EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); - PrintAddresses(accepted_sock, "ACCEPTED"); sockaddr_any sn; EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); EXPECT_NE(sn.get_addr(), nullptr); + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); + + m_ReadyCaller->get_future().wait(); if (sn.get_addr() != nullptr) { @@ -161,30 +165,8 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } - std::cout << "DoAccept: [LOCK-SIGNAL]\n"; - - // Travis makes problems here by unknown reason. Try waiting up to 10s - // until it's possible, otherwise simply give up. The intention is to - // prevent from closing too soon before the caller thread has a chance - // to perform required verifications. After 10s we can consider it enough time. - - int nms = 10; - while (!m_ReadyToCloseLock.try_lock()) - { - this_thread::sleep_for(milliseconds_from(100)); - std::cout << "(lock failed, retrying " << nms << ")...\n"; - if (--nms == 0) - break; - } - - std::cout << "(signaling...)\n"; - CSync::notify_all_relaxed(m_ReadyToClose); - if (nms) - { - std::cout << "(unlocking...)\n"; - m_ReadyToCloseLock.unlock(); - } - std::cout << "DoAccept: [UNLOCKED] " << nms << "\n"; + std::cout << "DoAccept: ready accept - promise SET\n"; + m_ReadyAccept->set_value(); srt_close(accepted_sock); return sn; @@ -356,12 +338,11 @@ TEST_F(TestIPv6, plsize_faux_v6) // Sleeping isn't reliable so do a dampened spinlock here. // This flag also confirms that the caller acquired the mutex and will // unlock it for CV waiting - so we can proceed to notifying it. - do std::this_thread::sleep_for(milliseconds(100)); while (!m_CallerStarted); + m_CallerStarted->get_future().wait(); // Just in case of a test failure, kick CV to avoid deadlock - std::cout << "TEST: [LOCK-SIGNAL]\n"; - CSync::lock_notify_all(m_ReadyToClose, m_ReadyToCloseLock); - std::cout << "TEST: [UNLOCKED]\n"; + std::cout << "TEST: [PROMISE-SET]\n"; + m_ReadyAccept->set_value(); client.join(); } From bc4059c708fa846b193440ab3a844ce47e4c09c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 7 Mar 2023 17:11:17 +0100 Subject: [PATCH 079/517] Fixed filtering-out IPv6 tests for Travis --- .travis.yml | 2 +- test/test_ipv6.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eaca3a571..26c6eaa41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,7 +71,7 @@ matrix: - BUILD_TYPE=Release - BUILD_OPTS='-DENABLE_MONOTONIC_CLOCK=ON' script: - - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.v6_calls_v6*:ReuseAddr.ProtocolVersion:ReuseAddr.*6" ; # Tests to skip due to lack of IPv6 support + - TESTS_IPv6="TestMuxer.IPv4_and_IPv6:TestIPv6.*6:ReuseAddr.ProtocolVersion:ReuseAddr.*6" ; # Tests to skip due to lack of IPv6 support - if [ "$TRAVIS_COMPILER" == "x86_64-w64-mingw32-g++" ]; then export CC="x86_64-w64-mingw32-gcc"; export CXX="x86_64-w64-mingw32-g++"; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 4a464f1c4..7c9086c88 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -211,7 +211,7 @@ TEST_F(TestIPv6, v4_calls_v6_mapped) client.join(); } -TEST_F(TestIPv6, v6_calls_v6_mapped) +TEST_F(TestIPv6, v6_calls_mapped_v6) { sockaddr_any sa (AF_INET6); sa.hport(m_listen_port); From 11574a03d4446ba11e3cc72cd168435080c03d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 9 Mar 2023 11:04:43 +0100 Subject: [PATCH 080/517] Fixed wrong comment --- srtcore/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c66492375..9d63586bf 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5619,8 +5619,8 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); // IMPORTANT: // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent - // over the current connection - which means that if any party is IPv6, then the maximum size - // is the one for IPv6 (1444). Only if both parties are IPv4, this maximum size is 1456. + // over the current connection - which means that if both parties are IPv6, then the maximum size + // is the one for IPv6 (1444). If any party is IPv4, this maximum size is 1456. // The family as the first argument is something different - it's for the header size in order // to calculate rate and statistics. From 88b9f33ffef7da24b1866c4c8b20d5f0c3130a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 10 May 2023 17:27:26 +0200 Subject: [PATCH 081/517] Fixed build break on Android --- srtcore/srt_c_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 21c5995ce..2e25acd08 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -126,7 +126,7 @@ SRTSOCKET srt_connect_bind(SRTSOCKET u, SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { - if (CUDT::isgroup(u)) + if (isgroup(u)) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); bool yes = 1; From 4b8df2cc051983ef8e2ddd82d1249e34ba201656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 7 Jun 2023 10:28:46 +0200 Subject: [PATCH 082/517] [core][apps] Implemented the socket close reason feautre --- srtcore/api.cpp | 151 +++++++++++++++++++++++++++++++++++++---- srtcore/api.h | 27 +++++++- srtcore/atomic_clock.h | 6 ++ srtcore/core.cpp | 86 ++++++++++++++++++++--- srtcore/core.h | 26 +++++-- srtcore/group.cpp | 5 +- srtcore/packet.cpp | 2 +- srtcore/queue.cpp | 3 +- srtcore/srt.h | 33 +++++++++ srtcore/srt_c_api.cpp | 29 +++++++- testing/testmedia.cpp | 88 ++++++++++++++++++++++-- 11 files changed, 415 insertions(+), 41 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 7ec8ff570..94d813cae 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -117,14 +117,14 @@ SRT_SOCKSTATUS srt::CUDTSocket::getStatus() } // [[using locked(m_GlobControlLock)]] -void srt::CUDTSocket::breakSocket_LOCKED() +void srt::CUDTSocket::breakSocket_LOCKED(int reason) { // This function is intended to be called from GC, // under a lock of m_GlobControlLock. m_UDT.m_bBroken = true; m_UDT.m_iBrokenCounter = 0; HLOGC(smlog.Debug, log << "@" << m_SocketID << " CLOSING AS SOCKET"); - m_UDT.closeInternal(); + m_UDT.closeInternal(reason); setClosed(); } @@ -813,7 +813,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, #endif SRTSOCKET id = ns->m_SocketID; - ns->core().closeInternal(); + ns->core().closeInternal(SRT_CLS_LATE); ns->setClosed(); // The mapped socket should be now unmapped to preserve the situation that @@ -911,6 +911,37 @@ SRT_SOCKSTATUS srt::CUDTUnited::getStatus(const SRTSOCKET u) return i->second->getStatus(); } +int srt::CUDTUnited::getCloseReason(const SRTSOCKET u, SRT_CLOSE_INFO& info) +{ + // protects the m_Sockets structure + ScopedLock cg(m_GlobControlLock); + + // We need to search for the socket in: + // m_Sockets, if it is somehow still alive, + // m_ClosedSockets, if it's when it should be, + // m_ClosedDatabase, if it has been already garbage-collected and deleted. + + sockets_t::const_iterator i = m_Sockets.find(u); + if (i != m_Sockets.end()) + { + i->second->core().copyCloseInfo((info)); + return 0; + } + + i = m_ClosedSockets.find(u); + if (i != m_ClosedSockets.end()) + { + i->second->core().copyCloseInfo((info)); + } + + map::iterator c = m_ClosedDatabase.find(u); + if (c == m_ClosedDatabase.end()) + return -1; + + info = c->second.info; + return 0; +} + int srt::CUDTUnited::bind(CUDTSocket* s, const sockaddr_any& name) { ScopedLock cg(s->m_ControlLock); @@ -1811,7 +1842,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i continue; // This will also automatically remove it from the group and all eids - close(s); + close(s, SRT_CLS_INTERNAL); } // There's no possibility to report a problem on every connection @@ -1895,7 +1926,7 @@ int srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, i return 0; } -int srt::CUDTUnited::close(const SRTSOCKET u) +int srt::CUDTUnited::close(const SRTSOCKET u, int reason) { #if ENABLE_BONDING if (u & SRTGROUP_MASK) @@ -1910,7 +1941,7 @@ int srt::CUDTUnited::close(const SRTSOCKET u) if (!s) throw CUDTException(MJ_NOTSUP, MN_SIDINVAL, 0); - return close(s); + return close(s, reason); } #if ENABLE_BONDING @@ -1961,7 +1992,54 @@ void srt::CUDTUnited::deleteGroup_LOCKED(CUDTGroup* g) } #endif -int srt::CUDTUnited::close(CUDTSocket* s) +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::recordCloseReason(CUDTSocket* s) +{ + CloseInfo ci; + ci.info.agent = SRT_CLOSE_REASON(s->core().m_AgentCloseReason.load()); + ci.info.peer = SRT_CLOSE_REASON(s->core().m_PeerCloseReason.load()); + ci.info.time = s->core().m_CloseTimeStamp.load().time_since_epoch().count(); + + m_ClosedDatabase[s->m_SocketID] = ci; + + // As a DOS attack prevention, do not allow to keep more than 10 records. + // In a normal functioning of the application this shouldn't be necessary, + // but it is still needed that a record of a dead socket is kept for + // 10 gc cycles more to ensure that the application can obtain it even after + // the socket has been physically removed. But if we don't limit the number + // of these records, this could be vulnerable for DOS attack if the user + // forces the application to create and close SRT sockets very quickly. + // Hence remove the oldest record, which can be recognized from the `time` + // field, if the number of records exceeds 10. + if (m_ClosedDatabase.size() > MAX_CLOSE_RECORD_SIZE) + { + // remove the oldest one + // This can only be done by collecting all time info + map which; + + for (map::iterator x = m_ClosedDatabase.begin(); + x != m_ClosedDatabase.end(); ++x) + { + which[x->second.info.time] = x->first; + } + + map::iterator y = which.begin(); + size_t ntodel = m_ClosedDatabase.size() - MAX_CLOSE_RECORD_SIZE; + for (size_t i = 0; i < ntodel; ++i) + { + // Sanity check - should never happen because it's unlikely + // that two different sockets were closed exactly at the same + // nanosecond time. + if (y == which.end()) + break; + + m_ClosedDatabase.erase(y->second); + ++y; + } + } +} + +int srt::CUDTUnited::close(CUDTSocket* s, int reason) { HLOGC(smlog.Debug, log << s->core().CONID() << "CLOSE. Acquiring control lock"); ScopedLock socket_cg(s->m_ControlLock); @@ -1994,6 +2072,8 @@ int srt::CUDTUnited::close(CUDTSocket* s) // broadcast all "accept" waiting CSync::lock_notify_all(s->m_AcceptCond, s->m_AcceptLock); + + s->core().setAgentCloseReason(reason); } else { @@ -2003,7 +2083,7 @@ int srt::CUDTUnited::close(CUDTSocket* s) // may block INDEFINITELY. As long as it's acceptable to block the // call to srt_close(), and all functions in all threads where this // very socket is used, this shall not block the central database. - s->core().closeInternal(); + s->core().closeInternal(reason); // synchronize with garbage collection. HLOGC(smlog.Debug, @@ -2038,6 +2118,8 @@ int srt::CUDTUnited::close(CUDTSocket* s) } #endif + recordCloseReason(s); + m_Sockets.erase(s->m_SocketID); m_ClosedSockets[s->m_SocketID] = s; HLOGC(smlog.Debug, log << "@" << u << "U::close: Socket MOVED TO CLOSED for collecting later."); @@ -2639,6 +2721,12 @@ void srt::CUDTUnited::checkBrokenSockets() HLOGC(smlog.Debug, log << "checkBrokenSockets: moving BROKEN socket to CLOSED: @" << i->first); + // Note that this will not override the value that has been already + // set by some other functionality, only set it when not yet set. + s->core().setAgentCloseReason(SRT_CLS_INTERNAL); + + recordCloseReason(s); + // close broken connections and start removal timer s->setClosed(); tbc.push_back(i->first); @@ -2757,7 +2845,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) CUDTSocket* as = si->second; - as->breakSocket_LOCKED(); + as->breakSocket_LOCKED(SRT_CLS_DEADLSN); m_ClosedSockets[*q] = as; m_Sockets.erase(*q); } @@ -2783,7 +2871,7 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) m_ClosedSockets.erase(i); HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); - s->core().closeInternal(); + s->core().closeInternal(SRT_CLS_INTERNAL); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer."); @@ -2823,6 +2911,31 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) } } +void srt::CUDTUnited::checkTemporaryDatabases() +{ + ScopedLock cg(m_GlobControlLock); + + // It's not very efficient to collect first the keys of all + // elements to remove and then remove from the map by key. + + // In C++20 this is possible by doing + // m_ClosedDatabase.erase_if([](auto& c) { return --c.generation <= 0; }); + // but nothing equivalent in the earlier standards. + + vector expired; + + for (map::iterator c = m_ClosedDatabase.begin(); + c != m_ClosedDatabase.end(); ++c) + { + --c->second.generation; + if (c->second.generation <= 0) + expired.push_back(c->first); + } + + for (vector::iterator i = expired.begin(); i != expired.end(); ++i) + m_ClosedDatabase.erase(*i); +} + void srt::CUDTUnited::configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af) { w_m.m_mcfg = s->core().m_config; @@ -3278,14 +3391,20 @@ void* srt::CUDTUnited::garbageCollect(void* p) UniqueLock gclock(self->m_GCStopLock); + // START LIBRARY RUNNING LOOP while (!self->m_bClosing) { INCREMENT_THREAD_ITERATIONS(); self->checkBrokenSockets(); + self->checkTemporaryDatabases(); HLOGC(inlog.Debug, log << "GC: sleep 1 s"); self->m_GCStopCond.wait_for(gclock, seconds_from(1)); } + // END. + + // All the below code does the library cleanup, which should + // happen as a result of an application calling `srt_cleanup()`. // remove all sockets and multiplexers HLOGC(inlog.Debug, log << "GC: GLOBAL EXIT - releasing all pending sockets. Acquring control lock..."); @@ -3293,10 +3412,14 @@ void* srt::CUDTUnited::garbageCollect(void* p) { ScopedLock glock(self->m_GlobControlLock); + // Do not do generative expiry removal - there's no chance + // anyone can extract the close reason information since this point on. + self->m_ClosedDatabase.clear(); + for (sockets_t::iterator i = self->m_Sockets.begin(); i != self->m_Sockets.end(); ++i) { CUDTSocket* s = i->second; - s->breakSocket_LOCKED(); + s->breakSocket_LOCKED(SRT_CLS_CLEANUP); #if ENABLE_BONDING if (s->m_GroupOf) @@ -3705,11 +3828,11 @@ int srt::CUDT::connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t f } } -int srt::CUDT::close(SRTSOCKET u) +int srt::CUDT::close(SRTSOCKET u, int reason) { try { - return uglobal().close(u); + return uglobal().close(u, reason); } catch (const CUDTException& e) { @@ -4366,7 +4489,7 @@ int connect(SRTSOCKET u, const struct sockaddr* name, int namelen) int close(SRTSOCKET u) { - return srt::CUDT::close(u); + return srt::CUDT::close(u, SRT_CLS_API); } int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen) diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..1441e7bd9 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -186,7 +186,7 @@ class CUDTSocket /// from within the GC thread only (that is, only when /// the socket should be no longer visible in the /// connection, including for sending remaining data). - void breakSocket_LOCKED(); + void breakSocket_LOCKED(int reason); /// This makes the socket no longer capable of performing any transmission /// operation, but continues to be responsive in the connection in order @@ -233,6 +233,8 @@ class CUDTUnited // Public constants static const int32_t MAX_SOCKET_VAL = SRTGROUP_MASK - 1; // maximum value for a regular socket + static const int MAX_CLOSE_RECORD_TTL = 10; + static const size_t MAX_CLOSE_RECORD_SIZE = 10; public: enum ErrorHandling @@ -295,8 +297,8 @@ class CUDTUnited int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize); int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target); #endif - int close(const SRTSOCKET u); - int close(CUDTSocket* s); + int close(const SRTSOCKET u, int reason); + int close(CUDTSocket* s, int reason); void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen); void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen); int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout); @@ -488,6 +490,25 @@ class CUDTUnited CEPoll m_EPoll; // handling epoll data structures and events + struct CloseInfo + { + SRT_CLOSE_INFO info; + int generation; + + // The value here defines how many GC rolls it takes + // to remove the record. As GC rolls every 1 second, + // this is more-less the number of seconds this record + // will be alive AFTER you close the socket. + CloseInfo(): generation(MAX_CLOSE_RECORD_TTL) {} + }; + std::map m_ClosedDatabase; + + void checkTemporaryDatabases(); + void recordCloseReason(CUDTSocket* s); + +public: + int getCloseReason(const SRTSOCKET u, SRT_CLOSE_INFO& info); + private: CUDTUnited(const CUDTUnited&); CUDTUnited& operator=(const CUDTUnited&); diff --git a/srtcore/atomic_clock.h b/srtcore/atomic_clock.h index e01012313..840d35252 100644 --- a/srtcore/atomic_clock.h +++ b/srtcore/atomic_clock.h @@ -73,6 +73,12 @@ class AtomicClock dur.store(uint64_t(d.time_since_epoch().count())); } + void compare_exchange(const time_point_type& exp, const time_point_type& toset) + { + uint64_t val = exp.time_since_epoch().count(); + dur.compare_exchange(val, toset.time_since_epoch().count()); + } + AtomicClock& operator=(const time_point_type& s) { dur = s.time_since_epoch().count(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0e3cce0ee..17cbaeaab 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3703,7 +3703,9 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) { HLOGC(cnlog.Debug, log << CONID() << "startConnect: REJECTED by processConnectResponse - sending SHUTDOWN"); - sendCtrl(UMSG_SHUTDOWN); + setAgentCloseReason(SRT_CLS_LATE); + uint32_t reason[1] = { SRT_CLS_LATE }; + sendCtrl(UMSG_SHUTDOWN, NULL, reason, sizeof reason); } if (cst != CONN_CONTINUE && cst != CONN_CONFUSED) @@ -6052,7 +6054,7 @@ void srt::CUDT::addressAndSend(CPacket& w_pkt) } // [[using maybe_locked(m_GlobControlLock, if called from GC)]] -bool srt::CUDT::closeInternal() +bool srt::CUDT::closeInternal(int reason) { // NOTE: this function is called from within the garbage collector thread. @@ -6104,6 +6106,14 @@ bool srt::CUDT::closeInternal() } } + // Some calls of closeInternal pass UNKNOWN here, which means + // that they don't want to change the code. It should have been + // set already somewhere else, however. + if (reason != SRT_CLS_UNKNOWN) + { + setAgentCloseReason(reason); + } + // remove this socket from the snd queue if (m_bConnected) m_pSndQueue->m_pSndUList->remove(this); @@ -6186,7 +6196,8 @@ bool srt::CUDT::closeInternal() if (!m_bShutdown) { HLOGC(smlog.Debug, log << CONID() << "CLOSING - sending SHUTDOWN to the peer @" << m_PeerID); - sendCtrl(UMSG_SHUTDOWN); + int32_t shdata[1] = { reason }; + sendCtrl(UMSG_SHUTDOWN, NULL, shdata, sizeof shdata); } // Store current connection information. @@ -7843,7 +7854,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_SHUTDOWN: // 101 - Shutdown if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; - ctrlpkt.pack(pkttype); + ctrlpkt.pack(pkttype, NULL, rparam, size); ctrlpkt.m_iID = m_PeerID; nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); @@ -8333,6 +8344,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); m_bBroken = true; m_iBrokenCounter = 0; + setAgentCloseReason(SRT_CLS_IPE); return; } @@ -8742,6 +8754,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // this should not happen: attack or bug m_bBroken = true; m_iBrokenCounter = 0; + setAgentCloseReason(SRT_CLS_ROGUE); return; } @@ -8921,8 +8934,31 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) } } -void srt::CUDT::processCtrlShutdown() +void srt::CUDT::processCtrlShutdown(const CPacket& ctrlpkt) { + const uint32_t* data = (const uint32_t*) ctrlpkt.m_pcData; + const size_t data_len = ctrlpkt.getLength() / 4; + + int reason = 0; + + // This condition should be ALWAYS satisfied, it's only + // a sanity check before reading the data. Versions that + // do not support close reason will simply send 0 here because + // it's the padding 0 that is provided in every command + // that is not expected to carry any "body". It is acceptable + // that the old versions simply send 0 here, but then you + // can't have the UNKNOWN value in any of close reason + // fields because it means that it wasn't set. + if (data_len > 0) + { + reason = data[0]; + } + + if (reason == 0) + { + setPeerCloseReason(SRT_CLS_FALLBACK); + } + m_bShutdown = true; m_bClosing = true; m_bBroken = true; @@ -9008,7 +9044,7 @@ void srt::CUDT::processCtrl(const CPacket &ctrlpkt) break; case UMSG_SHUTDOWN: // 101 - Shutdown - processCtrlShutdown(); + processCtrlShutdown(ctrlpkt); break; case UMSG_DROPREQ: // 111 - Msg drop request @@ -9697,10 +9733,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) return true; } -// This is a close request, but called from the +// This is a close request, but called from the handler of the +// buffer overflow in live mode. void srt::CUDT::processClose() { - sendCtrl(UMSG_SHUTDOWN); + uint32_t res[1] = { SRT_CLS_OVERFLOW }; + sendCtrl(UMSG_SHUTDOWN, NULL, res, sizeof res); m_bShutdown = true; m_bClosing = true; @@ -11265,6 +11303,7 @@ bool srt::CUDT::checkExpTimer(const steady_clock::time_point& currtime, int chec if (m_bBreakAsUnstable || ((m_iEXPCount > COMM_RESPONSE_MAX_EXP) && (currtime - last_rsp_time > microseconds_from(PEER_IDLE_TMO_US)))) { + setAgentCloseReason(SRT_CLS_PEERIDLE); // // Connection is broken. // UDT does not signal any information about this instead of to stop quietly. @@ -11755,3 +11794,34 @@ void srt::CUDT::processKeepalive(const CPacket& ctrlpkt, const time_point& tsArr if (m_config.bDriftTracer) m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, -1); } + +// This function should be called when closing the socket internally. +void srt::CUDT::setAgentCloseReason(int reason) +{ + m_AgentCloseReason.compare_exchange(SRT_CLS_UNKNOWN, reason); + + // Do not touch m_PeerCloseReason, it should remain SRT_CLS_UNKNOWN. + // If this reason is already set to some value, then m_AgentCloseReason + // should have been already set to SRT_CLS_PEER. + + m_CloseTimeStamp.compare_exchange(time_point(), steady_clock::now()); +} + +// This function should be called in a handler of UMSG_SHUTDOWN. +void srt::CUDT::setPeerCloseReason(int reason) +{ + m_AgentCloseReason.compare_exchange(SRT_CLS_UNKNOWN, SRT_CLS_PEER); + if (m_AgentCloseReason == SRT_CLS_PEER) + { + m_PeerCloseReason.compare_exchange(SRT_CLS_UNKNOWN, reason); + + m_CloseTimeStamp.compare_exchange(time_point(), steady_clock::now()); + } +} + +void srt::CUDT::copyCloseInfo(SRT_CLOSE_INFO& info) +{ + info.agent = SRT_CLOSE_REASON(m_AgentCloseReason.load()); + info.peer = SRT_CLOSE_REASON(m_PeerCloseReason.load()); + info.time = m_CloseTimeStamp.load().time_since_epoch().count(); +} diff --git a/srtcore/core.h b/srtcore/core.h index e7ca57c50..7df4c1cdc 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -205,7 +205,7 @@ class CUDT #if ENABLE_BONDING static int connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG links [], int arraysize); #endif - static int close(SRTSOCKET u); + static int close(SRTSOCKET u, int reason); static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen); static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen); static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen); @@ -432,7 +432,11 @@ class CUDT SRTU_PROPERTY_RR(sync::Condition*, recvTsbPdCond, &m_RcvTsbPdCond); /// @brief Request a socket to be broken due to too long instability (normally by a group). - void breakAsUnstable() { m_bBreakAsUnstable = true; } + void breakAsUnstable() + { + m_bBreakAsUnstable = true; + setAgentCloseReason(SRT_CLS_UNSTABLE); + } void ConnectSignal(ETransmissionEvent tev, EventSlot sl); void DisconnectSignal(ETransmissionEvent tev); @@ -573,10 +577,13 @@ class CUDT /// Close the opened UDT entity. - bool closeInternal(); + bool closeInternal(int reason); void updateBrokenConnection(); void completeBrokenConnectionDependencies(int errorcode); + void setAgentCloseReason(int reason); + void setPeerCloseReason(int reason); + /// Request UDT to send out a data block "data" with size of "len". /// @param data [in] The address of the application data to be sent. /// @param len [in] The size of the data block. @@ -793,6 +800,15 @@ class CUDT sync::atomic m_bBreakAsUnstable; // A flag indicating that the socket should become broken because it has been unstable for too long. sync::atomic m_bPeerHealth; // If the peer status is normal sync::atomic m_RejectReason; + + // If the socket was closed by some reason locally, the reason is + // in m_AgentCloseReason and the m_PeerCloseReason is then SRT_CLS_UNKNOWN. + // If the socket was closed due to reception of UMSG_SHUTDOWN, the reason + // exctracted from the message is written to m_PeerCloseReason and the + // m_AgentCloseReason == SRT_CLS_PEER. + sync::atomic m_AgentCloseReason; + sync::atomic m_PeerCloseReason; + atomic_time_point m_CloseTimeStamp; // Time when the close reason was first set bool m_bOpened; // If the UDT entity has been opened // A counter (number of GC checks happening every 1s) to let the GC tag this socket as closed. sync::atomic m_iBrokenCounter; // If a broken socket still has data in the receiver buffer, it is not marked closed until the counter is 0. @@ -1042,7 +1058,7 @@ class CUDT void processCtrlDropReq(const CPacket& ctrlpkt); /// @brief Process incoming shutdown control packet - void processCtrlShutdown(); + void processCtrlShutdown(const CPacket& ctrlpkt); /// @brief Process incoming user defined control packet /// @param ctrlpkt incoming user defined packet void processCtrlUserDefined(const CPacket& ctrlpkt); @@ -1145,6 +1161,8 @@ class CUDT static const int SEND_LITE_ACK = sizeof(int32_t); // special size for ack containing only ack seq static const int PACKETPAIR_MASK = 0xF; + void copyCloseInfo(SRT_CLOSE_INFO&); + private: // Timers functions #if ENABLE_BONDING time_point m_tsFreshActivation; // GROUPS: time of fresh activation of the link, or 0 if past the activation phase or idle diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f4dfba1ba..835248968 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -944,7 +944,7 @@ void CUDTGroup::close() { try { - CUDT::uglobal().close(*i); + CUDT::uglobal().close(*i, SRT_CLS_INTERNAL); } catch (CUDTException&) { @@ -2231,6 +2231,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) LOGC(grlog.Error, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + ps->core().setAgentCloseReason(SRT_CLS_ROGUE); ps->core().m_bBroken = true; broken.insert(ps); continue; @@ -3350,7 +3351,7 @@ void CUDTGroup::sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx HLOGC(gslog.Debug, log << "grp/sendBackup: swait/ex on @" << (id) << " while waiting for any writable socket - CLOSING"); - CUDT::uglobal().close(s); // << LOCKS m_GlobControlLock, then GroupLock! + CUDT::uglobal().close(s, SRT_CLS_INTERNAL); // << LOCKS m_GlobControlLock, then GroupLock! } else { diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index 33555e7bb..c0be3aa2b 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -391,7 +391,7 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, case UMSG_SHUTDOWN: // 0101 - Shutdown // control info field should be none // but "writev" does not allow this - m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4); + m_PacketVector[PV_DATA].set(rparam, size); break; diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 4282965b4..9f5b178ce 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -963,7 +963,8 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst LinkStatusInfo fi = *i; fi.errorcode = SRT_ECONNREJ; toRemove.push_back(fi); - i->u->sendCtrl(UMSG_SHUTDOWN); + uint32_t res[1] = {SRT_CLS_DEADLSN}; + i->u->sendCtrl(UMSG_SHUTDOWN, NULL, res, sizeof res); } } diff --git a/srtcore/srt.h b/srtcore/srt.h index c30169f05..e36339d06 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -575,6 +575,37 @@ enum SRT_REJECT_REASON #define SRT_REJC_USERDEFINED 2000 // User defined error codes +enum SRT_CLOSE_REASON +{ + SRT_CLS_UNKNOWN, // Unset + SRT_CLS_INTERNAL, // Closed by internal reasons during connection attempt + SRT_CLS_PEER, // Received SHUTDOWN message from the peer + SRT_CLS_RESOURCE, // Problem with resource allocation + SRT_CLS_ROGUE, // Received wrong data in the packet + SRT_CLS_OVERFLOW, // Emergency close due to receiver buffer overflow + SRT_CLS_IPE, // Internal program error + SRT_CLS_API, // The application called srt_close(). + SRT_CLS_FALLBACK, // Used for peer that do not support close reason featur. + SRT_CLS_LATE, // Accepted-socket late-rejection or in-handshake rollback + SRT_CLS_CLEANUP, // All sockets are being closed due to srt_cleanup() call + SRT_CLS_DEADLSN, // This is an accepted socket off a dead listener + SRT_CLS_PEERIDLE, // Peer didn't send any packet for a time of SRTO_PEERIDLETIMEO + SRT_CLS_UNSTABLE, // Requested to be broken as unstable in Backup group + + SRT_CLS_E_SIZE +}; + +typedef struct SRT_CLOSE_INFO +{ + enum SRT_CLOSE_REASON agent; + enum SRT_CLOSE_REASON peer; + int64_t time; +} SRT_CLOSE_INFO; + +#define SRT_CLSC_INTERNAL 0 +#define SRT_CLSC_USER 100 + + // Logging API - specialization for SRT. // WARNING: This part is generated. @@ -783,6 +814,8 @@ SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_na const struct sockaddr* remote_name, int remote_namelen); SRT_API int srt_close (SRTSOCKET u); +SRT_API int srt_close_withreason(SRTSOCKET u, int reason); +SRT_API int srt_close_getreason(SRTSOCKET u, SRT_CLOSE_INFO* info); SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen); SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen); SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen); diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 885c80068..ac65aac9c 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -21,6 +21,7 @@ written by #include "common.h" #include "packet.h" #include "core.h" +#include "api.h" #include "utilities.h" using namespace std; @@ -154,7 +155,33 @@ int srt_close(SRTSOCKET u) return 0; } - return CUDT::close(u); + return CUDT::close(u, SRT_CLS_API); +} + +int srt_close_withreason(SRTSOCKET u, int reason) +{ + SRT_SOCKSTATUS st = srt_getsockstate(u); + + if ((st == SRTS_NONEXIST) || + (st == SRTS_CLOSED) || + (st == SRTS_CLOSING) ) + { + // It's closed already. Do nothing. + return 0; + } + + if (reason < SRT_CLSC_USER) + reason = SRT_CLS_API; + + return CUDT::close(u, reason); +} + +int srt_close_getreason(SRTSOCKET u, SRT_CLOSE_INFO* info) +{ + if (!info || u == SRT_INVALID_SOCK) + return -1; + + return CUDT::uglobal().getCloseReason(u, *info); } int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 34d2bdc9b..505614144 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -62,6 +62,50 @@ bool transmit_use_sourcetime = false; int transmit_retry_connect = 0; bool transmit_retry_always = false; +struct CloseReasonMap +{ + map at; + + CloseReasonMap() + { + at[SRT_CLS_UNKNOWN] = "Unset"; + at[SRT_CLS_INTERNAL] = "Closed by internal reasons during connection attempt"; + at[SRT_CLS_PEER] = "Received SHUTDOWN message from the peer"; + at[SRT_CLS_RESOURCE] = "Problem with resource allocation"; + at[SRT_CLS_ROGUE] = "Received wrong data in the packet"; + at[SRT_CLS_OVERFLOW] = "Emergency close due to receiver buffer overflow"; + at[SRT_CLS_IPE] = "Internal program error"; + at[SRT_CLS_API] = "The application called srt_close()."; + at[SRT_CLS_FALLBACK] = "The peer doesn't support close reason feature."; + at[SRT_CLS_LATE] = "Accepted-socket late-rejection or in-handshake rollback"; + at[SRT_CLS_CLEANUP] = "All sockets are being closed due to srt_cleanup() call"; + at[SRT_CLS_DEADLSN] = "This was an accepted socket off a dead listener"; + at[SRT_CLS_PEERIDLE] = "Peer didn't send any packet for a time of SRTO_PEERIDLETIMEO"; + at[SRT_CLS_UNSTABLE] = "Requested to be broken as unstable in Backup group"; + } + + string operator[](SRT_CLOSE_REASON reason) + { + if (int(reason) >= SRT_CLSC_USER) + { + string extra; + if (reason == SRT_CLSC_USER) + extra = " - Application exit due to interrupted transmission"; + + if (reason == SRT_CLSC_USER + 1) + extra = " - Error during configuration, transmission not started"; + + return Sprint("User-defined reason #", reason - SRT_CLSC_USER, extra); + } + + auto p = at.find(reason); + if (p == at.end()) + return "UNDEFINED"; + return p->second; + } + +} g_close_reason; + // Do not unblock. Copy this to an app that uses applog and set appropriate name. //srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-test"); @@ -706,7 +750,7 @@ void SrtCommon::Init(string host, int port, string path, map par, if (m_bindsock != SRT_INVALID_SOCK) srt_close(m_bindsock); if (m_sock != SRT_INVALID_SOCK) - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); m_sock = m_bindsock = SRT_INVALID_SOCK; throw; } @@ -1328,7 +1372,7 @@ void SrtCommon::ConnectClient(string host, int port) continue; } - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); Error("srt_connect", reason); } break; @@ -1394,6 +1438,9 @@ void SrtCommon::ConnectClient(string host, int port) void SrtCommon::Error(string src, int reason, int force_result) { + SRT_CLOSE_INFO cli; + int cls = srt_close_getreason(m_sock, &cli); + int errnov = 0; const int result = force_result == 0 ? srt_getlasterror(&errnov) : force_result; if (result == SRT_SUCCESS) @@ -1416,9 +1463,23 @@ void SrtCommon::Error(string src, int reason, int force_result) else { if ( Verbose::on ) - Verb() << "FAILURE\n" << src << ": [" << result << "." << errnov << "] " << message; + Verb() << "FAILURE\n" << src << ": [" << result << "." << errnov << "] " << message; else - cerr << "\nERROR #" << result << "." << errnov << ": " << message << endl; + cerr << "\nERROR #" << result << "." << errnov << ": " << message << endl; + } + + if (cls == 0) + { + int64_t srt_now = srt_time_now(); + int64_t ago = srt_now - cli.time; + cerr << "CLOSE REASON: ->\n\tagent=" << cli.agent << " [" << g_close_reason[cli.agent] + << "]\n\tpeer=" << cli.peer << " [" << g_close_reason[cli.peer] + << "]\n\ttime=" << cli.time << " (" << fixed << (ago/1000000.0) << "s ago)" + << endl; + } + else + { + cerr << "(CLOSE REASON not found)\n"; } throw TransmissionError("error: " + src + ": " + message); @@ -1448,7 +1509,7 @@ void SrtCommon::SetupRendezvous(string adapter, string host, int port) int stat = srt_bind(m_sock, localsa.get(), localsa.size()); if (stat == SRT_ERROR) { - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER+1); Error("srt_bind"); } } @@ -1465,8 +1526,21 @@ void SrtCommon::Close() { Verb() << "SrtCommon: DESTROYING CONNECTION, closing socket (rt%" << m_sock << ")..."; srt_setsockflag(m_sock, SRTO_SNDSYN, &yes, sizeof yes); - srt_close(m_sock); + srt_close_withreason(m_sock, SRT_CLSC_USER); any = true; + + SRT_CLOSE_INFO cli; + int cls = srt_close_getreason(m_sock, &cli); + + if (cls == 0) + { + int64_t srt_now = srt_time_now(); + int64_t ago = srt_now - cli.time; + cerr << "POST-FACTUM CLOSE REASON: ->\n\tagent=" << cli.agent << " [" << g_close_reason[cli.agent] + << "]\n\tpeer=" << cli.peer << " [" << g_close_reason[cli.peer] + << "]\n\ttime=" << cli.time << " (" << fixed << (ago/1000000.0) << "s ago)" + << endl; + } } if (m_bindsock != SRT_INVALID_SOCK) @@ -1474,7 +1548,7 @@ void SrtCommon::Close() Verb() << "SrtCommon: DESTROYING SERVER, closing socket (ls%" << m_bindsock << ")..."; // Set sndsynchro to the socket to synch-close it. srt_setsockflag(m_bindsock, SRTO_SNDSYN, &yes, sizeof yes); - srt_close(m_bindsock); + srt_close_withreason(m_bindsock, SRT_CLSC_USER); any = true; } From 397dc7c08fe3eb21580a453483b4f1e5e5086d2e Mon Sep 17 00:00:00 2001 From: Guangqing Chen Date: Fri, 16 Jun 2023 00:15:07 +0800 Subject: [PATCH 083/517] [core] Fixed overrideSndSeqNo() not clear buffer. --- srtcore/buffer_snd.cpp | 12 ++++++++++++ srtcore/buffer_snd.h | 1 + srtcore/core.cpp | 16 ++++++++++++---- srtcore/group.cpp | 27 +++++++++++++++------------ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 26f885dd6..1fc90eb40 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -663,6 +663,18 @@ int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_ return (dpkts); } +int CSndBuffer::dropAll(int& w_bytes) +{ + ScopedLock bufferguard(m_BufLock); + const int dpkts = m_iCount; + w_bytes = m_iBytesCount; + m_pFirstBlock = m_pCurrBlock = m_pLastBlock; + m_iCount = 0; + m_iBytesCount = 0; + updAvgBufSize(steady_clock::now()); + return dpkts; +} + void CSndBuffer::increase() { int unitsize = m_pBuffer->m_iSize; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 4440b9bfd..13446258b 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -158,6 +158,7 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time); + int dropAll(int& bytes); void updAvgBufSize(const time_point& time); int getAvgBufSize(int& bytes, int& timespan); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 0e3cce0ee..df30b7fc9 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -9784,7 +9784,15 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) return false; } - // + int dbytes; + const int dpkts SRT_ATR_UNUSED = m_pSndBuffer->dropAll((dbytes)); + + enterCS(m_StatsLock); + m_stats.sndr.dropped.count(dbytes);; + leaveCS(m_StatsLock); + + m_pSndLossList->removeUpTo(CSeqNo::decseq(seq)); + // The peer will have to do the same, as a reaction on perceived // packet loss. When it recognizes that this initial screwing up // has happened, it should simply ignore the loss and go on. @@ -9796,9 +9804,9 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) // the latter is ahead with the number of packets already scheduled, but // not yet sent. - HLOGC(gslog.Debug, log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo - << " (unchanged)" - ); + HLOGC(gslog.Debug, + log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo + << " dropped-pkts=" << dpkts); return true; } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f4dfba1ba..3986aa1d5 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3798,7 +3798,7 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // This should resend all packets if (m_SenderBuffer.empty()) { - LOGC(gslog.Fatal, log << "IPE: sendBackupRexmit: sender buffer empty"); + LOGC(gslog.Fatal, log << core.CONID() << "IPE: sendBackupRexmit: sender buffer empty"); // Although act as if it was successful, otherwise you'll get connection break return 0; @@ -3830,8 +3830,9 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // packets that are in the past towards the scheduling sequence. skip_initial = -distance; LOGC(gslog.Warn, - log << "sendBackupRexmit: OVERRIDE attempt. Link seqno %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq - << " - DENIED; skip " << skip_initial << " pkts, " << m_SenderBuffer.size() << " pkts in buffer"); + log << core.CONID() << "sendBackupRexmit: OVERRIDE attempt. Link seqno %" << core.schedSeqNo() + << ", trying to send from seqno %" << curseq << " - DENIED; skip " << skip_initial << " pkts, " + << m_SenderBuffer.size() << " pkts in buffer"); } else { @@ -3840,11 +3841,11 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) // sequence with it first so that they go hand-in-hand with // sequences already used by the link from which packets were // copied to the backup buffer. - IF_HEAVY_LOGGING(int32_t old = core.schedSeqNo()); - const bool su SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); - HLOGC(gslog.Debug, - log << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq - << (su ? " - succeeded" : " - FAILED!")); + const int32_t old SRT_ATR_UNUSED = core.schedSeqNo(); + const bool success SRT_ATR_UNUSED = core.overrideSndSeqNo(curseq); + LOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: OVERRIDING seq %" << old << " with %" << curseq + << (success ? " - succeeded" : " - FAILED!")); } } @@ -3852,8 +3853,8 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) if (skip_initial >= m_SenderBuffer.size()) { LOGC(gslog.Warn, - log << "sendBackupRexmit: All packets were skipped. Nothing to send %" << core.schedSeqNo() << ", trying to send from seqno %" << curseq - << " - DENIED; skip " << skip_initial << " packets"); + log << core.CONID() << "sendBackupRexmit: All packets were skipped. Nothing to send %" << core.schedSeqNo() + << ", trying to send from seqno %" << curseq << " - DENIED; skip " << skip_initial << " packets"); return 0; // can't return any other state, nothing was sent } @@ -3869,14 +3870,16 @@ int CUDTGroup::sendBackupRexmit(CUDT& core, SRT_MSGCTRL& w_mc) { // Stop sending if one sending ended up with error LOGC(gslog.Warn, - log << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() << " and FAILED"); + log << core.CONID() << "sendBackupRexmit: sending from buffer stopped at %" << core.schedSeqNo() + << " and FAILED"); return -1; } } // Copy the contents of the last item being updated. w_mc = m_SenderBuffer.back().mc; - HLOGC(gslog.Debug, log << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); + HLOGC(gslog.Debug, + log << core.CONID() << "sendBackupRexmit: pre-sent collected %" << curseq << " - %" << w_mc.pktseq); return stat; } From 76581458b1def63b6f89a169bfdf8c6aa7b2cfff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 11 Sep 2023 09:44:24 +0200 Subject: [PATCH 084/517] Fixed a bug introduced during upstream merge --- srtcore/core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eff90c51d..a4fe7559c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5678,6 +5678,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") << " authtag=" << authtag); + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iPeerISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space. From 41a86bfc4252d67e560e4983265c0ee1802c9a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 11 Sep 2023 10:13:25 +0200 Subject: [PATCH 085/517] Fixed tests that should require IPv6 enabled --- docs/API/API-socket-options.md | 36 ++++++++++++++++++++-------------- test/test_ipv6.cpp | 4 ++++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index f6ca5caad..ec2f5400d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -940,19 +940,25 @@ This value is a sum of: For the default 1500 the "remaining space" part is effectively 1456 for IPv4 and 1444 for IPv6. -Note that the IP version used here is not the domain of the socket, but the -in-transmission IP version. This is IPv4 for a case when the current socket's -binding address is of IPv4 domain, or if it is IPv6, but the peer's address -is then an IPv6-mapped-IPv4 address. The in-transmission IPv6 is only if the -peer's address is a true IPv6 address. Hence it is not possible to deteremine -all these limitations until the connection is established. Parts of SRT that -must allocate any resources regarding this value prior to connecting are using -the layout as per IPv4 because this results in a greater available space for -the "remaining space". +Note that the IP version used here is not the domain of the underlying UDP +socket, but the in-transmission IP version. This is effectively IPv4 in the +following cases: + +* when the current socket's binding address is of IPv4 domain +* when the peer's address is an IPv6-mapped-IPv4 address + +The IPv6 in-transmission IP version is assumed only if the peer's address +is a true IPv6 address (not IPv4 mapped). It is then not possible to determine +what the payload size limit until the connection is established. Parts of the +SRT facilities that must allocate any resources according to this value prior +to connecting are using the layout as per IPv4 because this way they allocate +more space than needed in the worst case. This value can be set on both connection parties independently, but after connection this option gets an effectively negotiated value, which is the less -one from both parties. +one from both parties. If this effective value is too small on any of the +connection peers, the connection is rejected (or late-rejected on the caller +side). This value then effectively controls: @@ -987,12 +993,12 @@ over the link used for the connection). See also limitations for In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit for one single packet sending, and therefore bigger portions of data are internally split into smaller portions, each one using the maximum available -"remaining space". The best value for this case is then equal to the current -network device's MTU size. Setting a greater value is possible (maximum for the -system API is 65535), but it may lead to packet fragmentation on the system -level. This is highly unwanted in SRT because: +"remaining space". The best value of `SRTO_MSS` for this case is then equal to +the current network device's MTU size. Setting a greater value is possible +(maximum for the system API is 65535), but it may lead to packet fragmentation +on the system level. This is highly unwanted in SRT because: -* SRT does also its own fragmentation, so it would be counter-productive +* Here SRT does also its own fragmentation, so it would be counter-productive * It would use more system resources with no advantage * SRT is unaware of it, so the statistics will be slightly falsified diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index afc65e894..e8c14141d 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -272,6 +272,8 @@ TEST_F(TestIPv6, v6_calls_v4) TEST_F(TestIPv6, plsize_v6) { + SRTST_REQUIRES(IPv6); + SetupFileMode(); sockaddr_any sa (AF_INET6); @@ -319,6 +321,8 @@ TEST_F(TestIPv6, plsize_v4) TEST_F(TestIPv6, plsize_faux_v6) { + SRTST_REQUIRES(IPv6); + using namespace std::chrono; SetupFileMode(); From 45809bdbbdf3d979a3ecd7a4939653fb8a389554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 18 Sep 2023 13:08:01 +0200 Subject: [PATCH 086/517] Pre-refax of change-independent parts for 2677 --- srtcore/buffer_rcv.cpp | 9 +++++++-- srtcore/channel.cpp | 9 +++++++++ srtcore/common.cpp | 2 +- srtcore/common.h | 9 +++++++++ srtcore/congctl.cpp | 5 ++++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 1f46788aa..7ea0945bc 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -130,7 +130,7 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) , m_iPktsCount(0) - , m_uAvgPayloadSz(SRT_LIVE_DEF_PLSIZE) + , m_uAvgPayloadSz(0) { SRT_ASSERT(size < size_t(std::numeric_limits::max())); // All position pointers are integers } @@ -758,7 +758,12 @@ void CRcvBuffer::countBytes(int pkts, int bytes) m_iBytesCount += bytes; // added or removed bytes from rcv buffer m_iPktsCount += pkts; if (bytes > 0) // Assuming one pkt when adding bytes - m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + { + if (!m_uAvgPayloadSz) + m_uAvgPayloadSz = bytes; + else + m_uAvgPayloadSz = avg_iir<100>(m_uAvgPayloadSz, (unsigned) bytes); + } } void CRcvBuffer::releaseUnitInPos(int pos) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 091adf115..38ce5b598 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1016,6 +1016,15 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet if ((msg_flags & errmsgflg[i].first) != 0) flg << " " << errmsgflg[i].second; + if (msg_flags & MSG_TRUNC) + { + // Additionally show buffer information in this case + flg << " buffers: "; + for (size_t i = 0; i < CPacket::PV_SIZE; ++i) + { + flg << "[" << w_packet.m_PacketVector[i].iov_len << "] "; + } + } // This doesn't work the same way on Windows, so on Windows just skip it. #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index b621c8025..dc60b3db7 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -205,7 +205,7 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc { // Check if the peer address is a model of IPv4-mapped-on-IPv6. // If so, it means that the `ip` array should be interpreted as IPv4. - const bool is_mapped_ipv4 = checkMappedIPv4((uint16_t*)peer.sin6.sin6_addr.s6_addr); + const bool is_mapped_ipv4 = checkMappedIPv4(peer.sin6); sockaddr_in6* a = (&w_addr.sin6); diff --git a/srtcore/common.h b/srtcore/common.h index 5021fa5a8..de42a7d83 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1422,6 +1422,15 @@ inline std::string SrtVersionString(int version) bool SrtParseConfig(const std::string& s, SrtConfig& w_config); +bool checkMappedIPv4(const uint16_t* sa); + +inline bool checkMappedIPv4(const sockaddr_in6& sa) +{ + const uint16_t* addr = reinterpret_cast(&sa.sin6_addr.s6_addr); + return checkMappedIPv4(addr); +} + + } // namespace srt #endif diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 91c73d660..b9265c046 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -63,6 +63,7 @@ class LiveCC: public SrtCongestionControlBase int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec) srt::sync::atomic m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit size_t m_zMaxPayloadSize; + size_t m_zHeaderSize; // NAKREPORT stuff. int m_iMinNakInterval_us; // Minimum NAK Report Period (usec) @@ -81,6 +82,8 @@ class LiveCC: public SrtCongestionControlBase m_zMaxPayloadSize = parent->maxPayloadSize(); m_zSndAvgPayloadSize = m_zMaxPayloadSize; + m_zHeaderSize = parent->m_config.iMSS - parent->maxPayloadSize(); + m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) @@ -173,7 +176,7 @@ class LiveCC: public SrtCongestionControlBase void updatePktSndPeriod() { // packet = payload + header - const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE; + const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod << " by avg pktsize=" << m_zSndAvgPayloadSize From 0c3abe051623e70ff0d2c51e383e48b745d3d657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 18 Sep 2023 15:15:51 +0200 Subject: [PATCH 087/517] A fix from code review --- srtcore/api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 8ce383d84..5646e3e79 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3160,7 +3160,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons // We can't use maxPayloadSize() because this value isn't valid until the connection is established. // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. - size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + const size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 << " plsize=" << payload_size << " hsize=" << 1024); From ce2a043e86120b734d3ea98a13fe93d528700e29 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 08:38:11 +0200 Subject: [PATCH 088/517] Apply SOME suggestions from the doc review (others pending) Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 62 ++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index ec2f5400d..cb6b88f2e 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -947,20 +947,19 @@ following cases: * when the current socket's binding address is of IPv4 domain * when the peer's address is an IPv6-mapped-IPv4 address -The IPv6 in-transmission IP version is assumed only if the peer's address -is a true IPv6 address (not IPv4 mapped). It is then not possible to determine -what the payload size limit until the connection is established. Parts of the -SRT facilities that must allocate any resources according to this value prior -to connecting are using the layout as per IPv4 because this way they allocate -more space than needed in the worst case. +The IPv6 transmission case is assumed only if the peer's address is a true IPv6 address +(not IPv4 mapped). It is then not possible to determine the payload size limit +until the connection is established. SRT operations that must allocate any +resources according to this value prior to connecting will assume IPv4 transmission +because this way, in the worst case, they allocate more space than needed . This value can be set on both connection parties independently, but after -connection this option gets an effectively negotiated value, which is the less -one from both parties. If this effective value is too small on any of the +connection `SRTO_MSS` gets a negotiated value, which is the lesser +of the two. If this effective value is too small for either of the connection peers, the connection is rejected (or late-rejected on the caller side). -This value then effectively controls: +This value then controls: * The maximum size of the payload in a single UDP packet ("remaining space"). @@ -970,24 +969,23 @@ in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it is that some buffer resources are allocated prior to the connection, so this value must fit both IPv4 and IPv6 for buffer memory allocation. -The default value 1500 matches the standard MTU size for network devices. It -is recommended that this value be set at maximum to the value of MTU size of -the network device that you will use for connection. - -Detailed recommendations for this value differ in the file and live mode. - -In the live mode a single call to `srt_send*` function may only send data -that fit in one packet. This size is defined by the `SRTO_PAYLOADSIZE` -option (defult: 1316) and it is also the size of the data in a single UDP -packet. To save memory space, you may want then to set MSS in live mode to -a value for which the "remaining space" matches `SRTO_PAYLOADSIZE` value (for -default 1316 it will be 1360 for IPv4 and 1372 for IPv6). This is not done by -default for security reasons: this may potentially lead to inability to read an -incoming UDP packet if its size is by some reason bigger than the negotiated MSS. -This may lead to misleading situations and hard to detect errors. You should -set such a value only if the peer is trusted (that is, you can be certain that -it will never come to a situation of having received an oversized UDP packet -over the link used for the connection). See also limitations for +The default value of 1500 corresponds to the standard MTU size for network devices. It +is recommended that this value be set to the maximum MTU size of +the network device that you will use for the connection. + +The recommendations for the value of `SRTO_MSS` differ between file and live modes. + +In live mode a single call to the `srt_send*` function may only send data +that fits in one packet. This size is defined by the `SRTO_PAYLOADSIZE` +option (defult: 1316), and it is also the size of the data in a single UDP +packet. To save memory space, you may want then to set `SRTO_MSS` in live mode to +a value for which the "remaining space" matches the `SRTO_PAYLOADSIZE` value (for +the default value of 1316 this will be 1360 for IPv4 and 1372 for IPv6). For security reasons, +this is not done by default: it may potentially lead to the inability to read an incoming UDP +packet if its size is for some reason bigger than the negotiated MSS, which may in turn lead +to unpredictable behaviour and hard-to-detect errors. You should set such a value only if +the peer is trusted (that is, you can be certain that you will never receive an oversized UDP +packet over the link used for the connection). You should also consider the limitations of `SRTO_PAYLOADSIZE`. In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit @@ -998,12 +996,12 @@ the current network device's MTU size. Setting a greater value is possible (maximum for the system API is 65535), but it may lead to packet fragmentation on the system level. This is highly unwanted in SRT because: -* Here SRT does also its own fragmentation, so it would be counter-productive -* It would use more system resources with no advantage -* SRT is unaware of it, so the statistics will be slightly falsified +* SRT also performs its own fragmentation, so it would be counter-productive +* It would use more system resources to no advantage +* SRT is unaware of it, so the resulting statistics would be slightly misleading -The system-level packet fragmentation cannot be however reliably turned off; -the best approach is then to avoid it by using appropriate parameters. +System-level packet fragmentation cannot be reliably turned off, +so safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) From 4bb7b47998c5e1c82b8649b95ba2150e5bd073e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 10:18:04 +0200 Subject: [PATCH 089/517] Added mutex spec to a function --- srtcore/core.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/srtcore/core.h b/srtcore/core.h index 6ae9c7e89..538df167c 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -668,6 +668,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_EXCLUDES(m_RcvBufferLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) From d1798784a1e6573461a9caa493603eba63ba2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 19 Sep 2023 13:05:27 +0200 Subject: [PATCH 090/517] Added more thread check entries --- srtcore/api.h | 2 ++ srtcore/core.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..9a5c6081c 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -123,6 +123,8 @@ class CUDTSocket void construct(); + // XXX Controversial as to whether it should be guarded by this lock. + // It is used in many places without the lock, and it is also atomic. SRT_ATTR_GUARDED_BY(m_ControlLock) sync::atomic m_Status; //< current socket state diff --git a/srtcore/core.h b/srtcore/core.h index 538df167c..b8a24032a 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -318,6 +318,7 @@ class CUDT #endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + SRT_ATTR_REQUIRES(m_RecvAckLock) int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } @@ -365,6 +366,7 @@ class CUDT /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @returns The number of packets in flight belonging to the interval [0; ...) + SRT_ATTR_REQUIRES(m_RecvAckLock) int32_t getFlightSpan() const { return getFlightSpan(m_iSndLastAck, m_iSndCurrSeqNo); From 5c2c876b530a25b5c80f0d70306bea54d9416045 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:50:50 +0200 Subject: [PATCH 091/517] Apply suggestions from doc review (still pending) Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index cb6b88f2e..21da9c2a3 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -927,17 +927,17 @@ The default value is 0x010000 (SRT v1.0.0). Maximum Segment Size. This value represents the maximum size of a UDP packet sent by the system. Therefore the value of `SRTO_MSS` must not exceed the values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer -allocation and rate calculation using packet counter assuming fully filled +allocation and rate calculation using a packet counter that assumes fully filled packets. This value is a sum of: -* IP header (20 bytes for IPv4 or 32 bytes for IPv6) +* IP header (20 bytes for IPv4, or 32 bytes for IPv6) * UDP header (8 bytes) * SRT header (16 bytes) * remaining space (as the maximum payload size available for a packet) -For the default 1500 the "remaining space" part is effectively 1456 for IPv4 +For the default 1500 the "remaining space" is effectively 1456 for IPv4 and 1444 for IPv6. Note that the IP version used here is not the domain of the underlying UDP @@ -951,7 +951,7 @@ The IPv6 transmission case is assumed only if the peer's address is a true IPv6 (not IPv4 mapped). It is then not possible to determine the payload size limit until the connection is established. SRT operations that must allocate any resources according to this value prior to connecting will assume IPv4 transmission -because this way, in the worst case, they allocate more space than needed . +because this way, in the worst case, they allocate more space than needed. This value can be set on both connection parties independently, but after connection `SRTO_MSS` gets a negotiated value, which is the lesser @@ -988,8 +988,8 @@ the peer is trusted (that is, you can be certain that you will never receive an packet over the link used for the connection). You should also consider the limitations of `SRTO_PAYLOADSIZE`. -In the file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit -for one single packet sending, and therefore bigger portions of data are +In file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for sending a single packet, and therefore bigger portions of data are internally split into smaller portions, each one using the maximum available "remaining space". The best value of `SRTO_MSS` for this case is then equal to the current network device's MTU size. Setting a greater value is possible From 0403820d6dbb1073dec6e0282d805526a0d7c838 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:51:30 +0200 Subject: [PATCH 092/517] Update doc review (still pending) --- docs/API/API-socket-options.md | 55 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index 21da9c2a3..f4581c358 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1155,45 +1155,52 @@ encrypted connection, they have to simply set the same passphrase. | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | -Sets the data limitation mode and the maximum data size for sending at once. +Sets the mode that determines the limitations on how data is sent, including the maximum +size of payload data sent within a single UDP packet. This option can be only set prior +to connecting, but it can be read also after the connection has been established. -The default value is 0 in the file mode and 1316 in live mode (this is one of -the options modified together with `SRTO_TRANSTYPE`). +The default value is 1316 in live mode (which is default) and 0 in file mode (when file +mode is set through the `SRTO_TRANSTYPE` option). -If the value is 0, this means a "file mode", in which the call to `srt_send*` -is not limited to a size fitting in one single packet, that is, the supplied -data will be split into multiple pieces fitting in a single UDP packet, if -necessary, as well as every packet will use the maximum space available -in a UDP packet (except the last in the stream or in the message) according to -the `SRTO_MSS` setting and others that may influence this size (such as -`SRTO_PACKETFILTER` and `SRTO_CRYPTOMODE`). +In file mode (`SRTO_PAYLOADSIZE` = 0) the call to `srt_send*` is not limited to the size +of a single packet. If necessary, the supplied data will be split into multiple pieces, +each fitting into a single UDP packet. Every data payload (except the last one in the +stream or in the message) will use the maximum space available in a UDP packet, +as determined by `SRTO_MSS` and other settings that may influence this size +(such as [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) and +[`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)). -If the value is greater than 0, this means a "live mode", and the value -defines the maximum size of: +Also when this option is set to 0 prior to connecting, then reading this option +from a connected socket will return the maximum size of the payload that fits +in a single packet according to the current connection parameters. -* the single call to a sending function (`srt_send*`) -* the payload supplied in every single data packet +In live mode (`SRTO_PAYLOADSIZE` > 0) the value defines the maximum size of: + +* a single call to a sending function (`srt_send*`) +* the payload supplied in each data packet + +as well as the minimum size of the buffer used for the `srt_recv*` call. This value can be set to a greater value than the default 1316, but the maximum possible value is limited by the following factors: -* 1500 is the default MSS (see `SRTO_MSS`), including headers, which are: - * 20 bytes for IPv4 or 32 bytes for IPv6 +* 1500 bytes is the default MSS (see [`SRTO_MSS`](#SRTO_MSS)), including headers, which occupy: + * 20 bytes for IPv4, or 32 bytes for IPv6 * 8 bytes for UDP * 16 bytes for SRT -This alone gives the limit of 1456 for IPv4 and 1444 for IPv6. This limit may -be however further decreased in the following cases: +This alone gives a limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be further decreased in the following cases: -* 4 bytes reserved for FEC, if you use the builtin FEC packet filter (see `SRTO_PACKETFILTER`) -* 16 bytes reserved for the authentication tag, if you use AES GCM (see `SRTO_CRYPTOMODE`) +* 4 bytes reserved for FEC, if you use the built in FEC packet filter (see [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER)) +* 16 bytes reserved for the authentication tag, if you use AES GCM (see [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)) -**WARNING**: The option setter will reject the setting if this value is too -great, but note that not every limitation can be checked prior to connection. +**WARNING**: The party setting the options will reject a value that is too +large, but note that not every limitation can be checked prior to connection. This includes: -* MSS defined by the peer, which may override MSS set in the agent -* The in-transmission IP version - see `SRTO_MSS` for details +* the MSS value defined by a peer, which may override the MSS set by an agent +* the in-transmission IP version (see [SRTO_MSS](#SRTO_MSS) for details) These values also influence the "remaining space" in the packet to be used for payload. If during the handshake it turns out that this "remaining space" is From e02d85a886ad2ee1d4f093d6a259af828fc23ae2 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 19 Sep 2023 16:52:25 +0200 Subject: [PATCH 093/517] Apply suggestions from doc review (complete) Co-authored-by: stevomatthews --- docs/API/API-socket-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index f4581c358..4c0c9640b 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -1001,7 +1001,7 @@ on the system level. This is highly unwanted in SRT because: * SRT is unaware of it, so the resulting statistics would be slightly misleading System-level packet fragmentation cannot be reliably turned off, -so safest approach is to avoid it by using appropriate parameters. +so the safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) From 6074f219ef53432c8a5ff9c4d6242116a2e848e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 2 Oct 2023 11:25:05 +0200 Subject: [PATCH 094/517] Added extra v6-to-v6 test to check correct payload size --- srtcore/core.cpp | 2 +- test/test_file_transmission.cpp | 225 ++++++++++++++++++++++---------- 2 files changed, 157 insertions(+), 70 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 99e6df610..e3c29b651 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5670,7 +5670,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) // The family as the first argument is something different - it's for the header size in order // to calculate rate and statistics. - int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(m_TransferIPVersion); SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index a81fb301b..d28532cc0 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -201,11 +201,9 @@ TEST(FileTransmission, Upload) TEST(FileTransmission, Setup46) { - SRTST_REQUIRES(IPv6); - using namespace srt; - - srt_startup(); + SRTST_REQUIRES(IPv6); + TestInit srtinit; SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); @@ -213,98 +211,187 @@ TEST(FileTransmission, Setup46) srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); - try - { - // Setup a connection with IPv6 caller and IPv4 listener, - // then send data of 1456 size and make sure two packets were used. + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. - // So first configure a caller for IPv6 socket, capable of - // using IPv4. As the IP version isn't specified now when - // creating a socket, force binding explicitly. + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. - // This creates the "any" spec for IPv6 with port = 0 - sockaddr_any sa(AF_INET6); + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); - int ipv4_and_ipv6 = 0; - ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); - ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); - int connect_port = 5555; + int connect_port = 5555; - // Configure listener - sockaddr_in sa_lsn = sockaddr_in(); - sa_lsn.sin_family = AF_INET; - sa_lsn.sin_addr.s_addr = INADDR_ANY; - sa_lsn.sin_port = htons(connect_port); + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); - // Find unused a port not used by any other service. - // Otherwise srt_connect may actually connect. - int bind_res = -1; - for (connect_port = 5000; connect_port <= 5555; ++connect_port) + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) { - sa_lsn.sin_port = htons(connect_port); - bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); - if (bind_res == 0) - { - std::cout << "Running test on port " << connect_port << "\n"; - break; - } - - ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + std::cout << "Running test on port " << connect_port << "\n"; + break; } - ASSERT_GE(bind_res, 0); + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); - srt_listen(sock_lsn, 1); + srt_listen(sock_lsn, 1); - ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); - sa.hport(connect_port); + sa.hport(connect_port); - ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); - int sock_acp = -1; - ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); - const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 - char buffer[SIZE]; + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; - std::random_device rd; - std::mt19937 mtrd(rd()); - std::uniform_int_distribution dis(0, UINT8_MAX); + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + +} - for (size_t i = 0; i < SIZE; ++i) +TEST(FileTransmission, Setup66) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + // Require that the connection allows both IP versions. + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + ASSERT_NE(srt_setsockflag(sock_lsn, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_any sa_lsn(AF_INET6); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.hport(connect_port); + bind_res = srt_bind(sock_lsn, sa_lsn.get(), sa_lsn.size()); + if (bind_res == 0) { - buffer[i] = dis(mtrd); + std::cout << "Running test on port " << connect_port << "\n"; + break; } - EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); - char resultbuf[SIZE]; - EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + srt_listen(sock_lsn, 1); - // It should use the maximum payload size per packet reported from the option. - int payloadsize_back = 0; - int payloadsize_back_size = sizeof (payloadsize_back); - EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); - EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + ASSERT_EQ(inet_pton(AF_INET6, "::1", &sa.sin6.sin6_addr), 1); - SRT_TRACEBSTATS snd_stats, rcv_stats; - srt_bstats(sock_acp, &snd_stats, 0); - srt_bstats(sock_clr, &rcv_stats, 0); + sa.hport(connect_port); - EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); - EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + std::cout << "Connecting to: " << sa.str() << std::endl; - } - catch (...) + int connect_result = srt_connect(sock_clr, sa.get(), sa.size()); + ASSERT_EQ(connect_result, 0) << srt_getlasterror_str(); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), SRT_ERROR); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) { - srt_cleanup(); - throw; + buffer[i] = dis(mtrd); } - srt_cleanup(); -} + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); -// XXX Setup66 - establish an IPv6 to IPv6 connection and make sure max payload size is that of IPv6. + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET6); + std::cout << "Payload size: " << payloadsize_back << std::endl; + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + // We use the same data size that fit in 1 payload IPv4, but not IPv6. + // Therefore sending should be here split into two packets. + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 2); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 2); + +} From 920969bbf15189173948f1e0b11db3ba775b277d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 10 Oct 2023 14:35:45 +0200 Subject: [PATCH 095/517] Synchronized and improved --- apps/srt-file-transmit.cpp | 14 +-- apps/srt-live-transmit.cpp | 14 +-- apps/srt-tunnel.cpp | 22 ++-- common/devel_util.h | 23 +++- srtcore/api.cpp | 2 +- srtcore/core.h | 2 +- srtcore/group.h | 2 +- srtcore/srt.h | 13 +- srtcore/srt_c_api.cpp | 2 +- test/test_enforced_encryption.cpp | 192 +++++++++++++++--------------- test/test_epoll.cpp | 160 ++++++++++++------------- test/test_fec_rebuilding.cpp | 14 +-- test/test_file_transmission.cpp | 9 +- test/test_ipv6.cpp | 4 +- testing/srt-test-file.cpp | 6 +- testing/srt-test-live.cpp | 2 +- 16 files changed, 254 insertions(+), 227 deletions(-) diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..2bd22a019 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -309,7 +309,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -349,7 +349,7 @@ bool DoUpload(UriParser& ut, string path, string filename, s = tar->GetSRTSocket(); int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -391,7 +391,7 @@ bool DoUpload(UriParser& ut, string path, string filename, int st = tar->Write(buf.data() + shift, n, 0, out_stats); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; @@ -429,7 +429,7 @@ bool DoUpload(UriParser& ut, string path, string filename, size_t bytes; size_t blocks; int st = srt_getsndbuffer(s, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; @@ -490,7 +490,7 @@ bool DoDownload(UriParser& us, string directory, string filename, int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -528,7 +528,7 @@ bool DoDownload(UriParser& us, string directory, string filename, s = src->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, s, &events)) + if (srt_epoll_add_usock(pollid, s, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll" << endl; goto exit; @@ -593,7 +593,7 @@ bool DoDownload(UriParser& us, string directory, string filename, } int n = src->Read(cfg.chunk_size, packet, out_stats); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; goto exit; diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index 24ed33be7..e608034f9 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -518,7 +518,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - src->GetSRTSocket(), &events)) + src->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT source to poll, " << src->GetSRTSocket() << endl; @@ -527,7 +527,7 @@ int main(int argc, char** argv) break; case UriParser::UDP: if (srt_epoll_add_ssock(pollid, - src->GetSysSocket(), &events)) + src->GetSysSocket(), &events) == SRT_ERROR) { cerr << "Failed to add UDP source to poll, " << src->GetSysSocket() << endl; @@ -536,7 +536,7 @@ int main(int argc, char** argv) break; case UriParser::FILE: if (srt_epoll_add_ssock(pollid, - src->GetSysSocket(), &events)) + src->GetSysSocket(), &events) == SRT_ERROR) { cerr << "Failed to add FILE source to poll, " << src->GetSysSocket() << endl; @@ -566,7 +566,7 @@ int main(int argc, char** argv) { case UriParser::SRT: if (srt_epoll_add_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -639,7 +639,7 @@ int main(int argc, char** argv) SRTSOCKET ns = (issource) ? src->GetSRTSocket() : tar->GetSRTSocket(); int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; - if (srt_epoll_add_usock(pollid, ns, &events)) + if (srt_epoll_add_usock(pollid, ns, &events) == SRT_ERROR) { cerr << "Failed to add SRT client to poll, " << ns << endl; @@ -737,7 +737,7 @@ int main(int argc, char** argv) const int events = SRT_EPOLL_IN | SRT_EPOLL_ERR; // Disable OUT event polling when connected if (srt_epoll_update_usock(pollid, - tar->GetSRTSocket(), &events)) + tar->GetSRTSocket(), &events) == SRT_ERROR) { cerr << "Failed to add SRT destination to poll, " << tar->GetSRTSocket() << endl; @@ -781,7 +781,7 @@ int main(int argc, char** argv) std::shared_ptr pkt(new MediaPacket(transmit_chunk_size)); const int res = src->Read(transmit_chunk_size, *pkt, out_stats); - if (res == SRT_ERROR && src->uri.type() == UriParser::SRT) + if (res == int(SRT_ERROR) && src->uri.type() == UriParser::SRT) { if (srt_getlasterror(NULL) == SRT_EASYNCRCV) break; diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 569a0c26e..8f4a6b7db 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -419,7 +419,7 @@ void Engine::Worker() class SrtMedium: public Medium { - SRTSOCKET m_socket = SRT_ERROR; + SRTSOCKET m_socket = SRT_INVALID_SOCK; friend class Medium; public: @@ -440,10 +440,10 @@ class SrtMedium: public Medium { Verb() << "Closing SRT socket for " << uri(); lock_guard lk(access); - if (m_socket == SRT_ERROR) + if (m_socket == SRT_INVALID_SOCK) return; srt_close(m_socket); - m_socket = SRT_ERROR; + m_socket = SRT_INVALID_SOCK; } // Forwarded in order to separate the implementation from @@ -624,16 +624,16 @@ void SrtMedium::CreateListener() sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int stat = srt_bind(m_socket, sa.get(), sizeof sa); + SRTSTATUS stat = srt_bind(m_socket, sa.get(), sizeof sa); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_bind"); } stat = srt_listen(m_socket, backlog); - if ( stat == SRT_ERROR ) + if (stat == SRT_ERROR) { srt_close(m_socket); Error(UDT::getlasterror(), "srt_listen"); @@ -674,7 +674,7 @@ unique_ptr SrtMedium::Accept() { sockaddr_any sa; SRTSOCKET s = srt_accept(m_socket, (sa.get()), (&sa.len)); - if (s == SRT_ERROR) + if (s == SRT_INVALID_SOCK) { Error(UDT::getlasterror(), "srt_accept"); } @@ -734,8 +734,8 @@ void SrtMedium::Connect() { sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno()); - int st = srt_connect(m_socket, sa.get(), sizeof sa); - if (st == SRT_ERROR) + SRTSOCKET st = srt_connect(m_socket, sa.get(), sizeof sa); + if (st == SRT_INVALID_SOCK) Error(UDT::getlasterror(), "srt_connect"); ConfigurePost(m_socket); @@ -766,7 +766,7 @@ int SrtMedium::ReadInternal(char* w_buffer, int size) do { st = srt_recv(m_socket, (w_buffer), size); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { int syserr; if (srt_getlasterror(&syserr) == SRT_EASYNCRCV && !m_broken) @@ -885,7 +885,7 @@ Medium::ReadStatus Medium::Read(bytevector& w_output) void SrtMedium::Write(bytevector& w_buffer) { int st = srt_send(m_socket, w_buffer.data(), (int)w_buffer.size()); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { Error(UDT::getlasterror(), "srt_send"); } diff --git a/common/devel_util.h b/common/devel_util.h index 969390993..3c00ec356 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -1,4 +1,11 @@ +#include + +template +concept Streamable = requires(OS& os, T value) { + { os << value }; +}; + template struct IntWrapper { @@ -22,16 +29,23 @@ struct IntWrapper return v; } - template - friend T& operator<<(T& out, const IntWrapper& x) + bool operator<(const IntWrapper& w) const + { + return v < w.v; + } + + template + requires Streamable + friend Str& operator<<(Str& out, const IntWrapper& x) { out << x.v; return out; } - bool operator<(const IntWrapper& w) const + friend std::ostream& operator<<(std::ostream& out, const IntWrapper& x) { - return v < w.v; + out << x.v; + return out; } }; @@ -72,3 +86,4 @@ typedef IntWrapper SRTSOCKET; typedef IntWrapper SRTSTATUS; typedef IntWrapperLoose SRTSTATUS_LOOSE; + diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 076db0e5c..bcf7653eb 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -233,7 +233,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return ""; std::ostringstream os; - os << "@" << sock << ":"; + os << "@" << int(sock) << ":"; return os.str(); } diff --git a/srtcore/core.h b/srtcore/core.h index 16867404f..33f07195f 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -297,7 +297,7 @@ class CUDT { #if ENABLE_LOGGING std::ostringstream os; - os << "@" << m_SocketID << ": "; + os << "@" << int(m_SocketID) << ": "; return os.str(); #else return ""; diff --git a/srtcore/group.h b/srtcore/group.h index ead45707e..68b999961 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -740,7 +740,7 @@ class CUDTGroup { #if ENABLE_LOGGING std::ostringstream os; - os << "$" << m_GroupID << ":"; + os << "$" << int(m_GroupID) << ":"; return os.str(); #else return ""; diff --git a/srtcore/srt.h b/srtcore/srt.h index fc82ce8e9..682a6fe26 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -134,12 +134,21 @@ written by #define SRT_ATR_DEPRECATED #endif -// Invert this in order to retest if the symbolic constants +// Unblock this in order to retest if the symbolic constants // have been used properly. With this change the compiler will // detect every case when it wasn't. +// Important: you need to --use-c++-std=c++20 to compile SRT +// with this enabled. +//#define SRT_TEST_FORCED_CONSTANT + +#ifndef SRT_TEST_FORCED_CONSTANT +// This is normal and should be normally used. typedef int32_t SRTSOCKET; typedef int SRTSTATUS; -//#include "../common/devel_util.h" +#else +// Used for development only. +#include "../common/devel_util.h" +#endif #ifdef __cplusplus diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 9804eb3f6..011f277ae 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -75,7 +75,7 @@ SRTSTATUS srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::C SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; } SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } -SRTSTATUS srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); } +SRTSOCKET srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; } #endif diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 6fd284c23..cf363305a 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -64,21 +64,21 @@ enum TEST_CASE struct TestResultNonBlocking { - int connect_ret; - int accept_ret; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; int epoll_wait_ret; int epoll_event; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state [CHECK_SOCKET_COUNT]; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state [CHECK_SOCKET_COUNT]; }; struct TestResultBlocking { - int connect_ret; - int accept_ret; - int socket_state[CHECK_SOCKET_COUNT]; - int km_state[CHECK_SOCKET_COUNT]; + SRTSOCKET connect_ret; + SRTSOCKET accept_ret; + SRT_SOCKSTATUS socket_state[CHECK_SOCKET_COUNT]; + SRT_KM_STATE km_state[CHECK_SOCKET_COUNT]; }; @@ -137,35 +137,37 @@ static const std::string s_pwd_no(""); */ const int IGNORE_EPOLL = -2; -const int IGNORE_SRTS = -1; +const SRT_SOCKSTATUS IGNORE_SRTS = (SRT_SOCKSTATUS)-1; +const SRT_KM_STATE IGNORE_KMSTATE = (SRT_KM_STATE)-1; +const SRTSOCKET IGNORE_ACCEPT = (SRTSOCKET)-2; const TestCaseNonBlocking g_test_matrix_non_blocking[] = { - // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -185,31 +187,31 @@ const TestCaseNonBlocking g_test_matrix_non_blocking[] = */ const TestCaseBlocking g_test_matrix_blocking[] = { - // ENFORCEDENC | Password | | socket_state | KM State - // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener -/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, -/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}}, -/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, - -/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, -/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, -/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, -/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, -/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + // ENFORCEDENC | Password | | socket_state | KM State + // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener +/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, IGNORE_ACCEPT, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}}, +/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_KMSTATE}}}, +/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, + +/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}}, +/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}}, +/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, +/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}}, +/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SOCKID_CONNREQ, SRT_SOCKID_CONNREQ, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}}, }; @@ -240,13 +242,13 @@ class TestEnforcedEncryption ASSERT_NE(m_caller_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_SENDER, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_caller_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); m_listener_socket = srt_create_socket(); ASSERT_NE(m_listener_socket, SRT_INVALID_SOCK); ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SENDER, &s_no, sizeof s_no), SRT_ERROR); - ASSERT_NE(srt_setsockopt (m_listener_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR); // Will use this epoll to wait for srt_accept(...) const int epoll_out = SRT_EPOLL_IN | SRT_EPOLL_ERR; @@ -265,10 +267,10 @@ class TestEnforcedEncryption public: - int SetEnforcedEncryption(PEER_TYPE peer, bool value) + SRTSTATUS SetEnforcedEncryption(PEER_TYPE peer, bool value) { const SRTSOCKET &socket = peer == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); + return srt_setsockflag(socket, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes); } @@ -277,15 +279,15 @@ class TestEnforcedEncryption const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; bool optval; int optlen = sizeof optval; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_STATUS_OK); return optval ? true : false; } - int SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) + SRTSTATUS SetPassword(PEER_TYPE peer_type, const std::basic_string &pwd) { const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket; - return srt_setsockopt(socket, 0, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); + return srt_setsockflag(socket, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size()); } @@ -293,8 +295,8 @@ class TestEnforcedEncryption { int km_state = 0; int opt_size = sizeof km_state; - EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_SUCCESS); - + EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast(&km_state), &opt_size), SRT_STATUS_OK); + return km_state; } @@ -303,7 +305,7 @@ class TestEnforcedEncryption { int val = 0; int size = sizeof val; - EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_SUCCESS); + EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast(&val), &size), SRT_STATUS_OK); return val; } @@ -322,26 +324,26 @@ class TestEnforcedEncryption const bool is_blocking = std::is_same::value; if (is_blocking) { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR); } else { - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode - ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag( m_caller_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode + ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode } // Prepare input state const TestCase &test = GetTestMatrix(test_case); - ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_STATUS_OK); - ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_SUCCESS); - ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_SUCCESS); + ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_STATUS_OK); + ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_STATUS_OK); // Determine the subcase for the KLUDGE (check the behavior of the decryption failure) const bool case_pw_failure = test.password[PEER_CALLER] != test.password[PEER_LISTENER]; @@ -361,7 +363,7 @@ class TestEnforcedEncryption ASSERT_NE(srt_bind(m_listener_socket, psa, sizeof sa), SRT_ERROR); ASSERT_NE(srt_listen(m_listener_socket, 4), SRT_ERROR); - SRTSOCKET accepted_socket = -1; + SRTSOCKET accepted_socket = SRT_INVALID_SOCK; auto accepting_thread = std::thread([&] { const int epoll_event = WaitOnEpoll(expect); @@ -390,12 +392,12 @@ class TestEnforcedEncryption std::cerr << "[T] ACCEPT SUCCEEDED: @" << accepted_socket << "\n"; } - EXPECT_NE(accepted_socket, 0); + EXPECT_NE(accepted_socket, SRT_SOCKID_CONNREQ); if (expect.accept_ret == SRT_INVALID_SOCK) { EXPECT_EQ(accepted_socket, SRT_INVALID_SOCK); } - else if (expect.accept_ret != -2) + else if (expect.accept_ret != IGNORE_ACCEPT) { EXPECT_NE(accepted_socket, SRT_INVALID_SOCK); } @@ -441,10 +443,10 @@ class TestEnforcedEncryption } }); - const int connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); + const SRTSOCKET connect_ret = srt_connect(m_caller_socket, psa, sizeof sa); EXPECT_EQ(connect_ret, expect.connect_ret); - if (connect_ret == SRT_ERROR && connect_ret != expect.connect_ret) + if (connect_ret == SRT_INVALID_SOCK && connect_ret != expect.connect_ret) { std::cerr << "UNEXPECTED! srt_connect returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; @@ -516,7 +518,7 @@ class TestEnforcedEncryption std::cout << "W: " << epoll_res_w << std::endl; char buffer[1316] = {1, 2, 3, 4}; - ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), SRT_ERROR); + ASSERT_NE(srt_sendmsg2(m_caller_socket, buffer, sizeof buffer, nullptr), int(SRT_ERROR)); std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -615,13 +617,13 @@ int TestEnforcedEncryption::WaitOnEpoll(const TestResultN } std::cerr << std::endl; - // Expect: -2 means that + // Expect: IGNORE_EPOLL means that you should not check the result. if (expect.epoll_wait_ret != IGNORE_EPOLL) { EXPECT_EQ(epoll_res, expect.epoll_wait_ret); } - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; return 0; @@ -687,8 +689,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) { #ifdef SRT_ENABLE_ENCRYPTION // Empty string sets password to none - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_STATUS_OK); EXPECT_EQ(SetPassword(PEER_CALLER, std::string("too_short")), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("too_short")), SRT_ERROR); @@ -706,8 +708,8 @@ TEST_F(TestEnforcedEncryption, PasswordLength) EXPECT_EQ(SetPassword(PEER_CALLER, long_pwd), SRT_ERROR); EXPECT_EQ(SetPassword(PEER_LISTENER, long_pwd), SRT_ERROR); - EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_SUCCESS); - EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_SUCCESS); + EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_STATUS_OK); + EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_STATUS_OK); #else EXPECT_EQ(SetPassword(PEER_CALLER, "whateverpassword"), SRT_ERROR); #endif @@ -723,8 +725,8 @@ TEST_F(TestEnforcedEncryption, SetGetDefault) EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), true); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), true); - EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_SUCCESS); - EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_SUCCESS); + EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_STATUS_OK); + EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_STATUS_OK); EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), false); EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), false); diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 72946b736..63b443493 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -22,9 +22,9 @@ TEST(CEPoll, InfiniteWait) ASSERT_EQ(srt_epoll_wait(epoll_id, nullptr, nullptr, nullptr, nullptr, -1, - 0, 0, 0, 0), SRT_ERROR); + 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, WaitNoSocketsInEpoll) @@ -41,9 +41,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -56,9 +56,9 @@ TEST(CEPoll, WaitNoSocketsInEpoll2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -67,11 +67,11 @@ TEST(CEPoll, WaitEmptyCall) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -80,9 +80,9 @@ TEST(CEPoll, WaitEmptyCall) ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); ASSERT_EQ(srt_epoll_wait(epoll_id, 0, NULL, 0, NULL, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } TEST(CEPoll, UWaitEmptyCall) @@ -90,11 +90,11 @@ TEST(CEPoll, UWaitEmptyCall) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -102,9 +102,9 @@ TEST(CEPoll, UWaitEmptyCall) const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR); - ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -113,14 +113,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -136,9 +136,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) SRTSOCKET write[2]; ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen, - -1, 0, 0, 0, 0), SRT_ERROR); + -1, 0, 0, 0, 0), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -147,14 +147,14 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -165,9 +165,9 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) SRT_EPOLL_EVENT events[2]; - ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR); + ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), int(SRT_ERROR)); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -176,11 +176,11 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int epoll_id = srt_epoll_create(); ASSERT_GE(epoll_id, 0); @@ -189,7 +189,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) /* We intentionally pass the wrong socket ID. The error should be returned.*/ ASSERT_EQ(srt_epoll_add_usock(epoll_id + 1, client_sock, &epoll_out), SRT_ERROR); - EXPECT_EQ(srt_epoll_release(epoll_id), 0); + EXPECT_EQ(srt_epoll_release(epoll_id), SRT_STATUS_OK); } @@ -199,14 +199,14 @@ TEST(CEPoll, HandleEpollEvent) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -224,7 +224,7 @@ TEST(CEPoll, HandleEpollEvent) set* rval = &readset; set* wval = &writeset; - ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), SRT_ERROR); + ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), int(SRT_ERROR)); try { @@ -260,19 +260,19 @@ TEST(CEPoll, NotifyConnectionBreak) // 1. Prepare client SRTSOCKET client_sock = srt_create_socket(); - ASSERT_NE(client_sock, SRT_ERROR); + ASSERT_NE(client_sock, SRT_INVALID_SOCK); const int yes SRT_ATR_UNUSED = 1; const int no SRT_ATR_UNUSED = 0; - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int client_epoll_id = srt_epoll_create(); ASSERT_GE(client_epoll_id, 0); const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR; /* We intentionally pass the wrong socket ID. The error should be returned.*/ - EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_SUCCESS); + EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_STATUS_OK); sockaddr_in sa_client; memset(&sa_client, 0, sizeof sa_client); @@ -282,10 +282,10 @@ TEST(CEPoll, NotifyConnectionBreak) // 2. Prepare server SRTSOCKET server_sock = srt_create_socket(); - ASSERT_NE(server_sock, SRT_ERROR); + ASSERT_NE(server_sock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(server_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect const int server_epoll_id = srt_epoll_create(); ASSERT_GE(server_epoll_id, 0); @@ -320,13 +320,13 @@ TEST(CEPoll, NotifyConnectionBreak) 0, 0, 0, 0); EXPECT_EQ(epoll_res, 1); - if (epoll_res == SRT_ERROR) + if (epoll_res == int(SRT_ERROR)) { std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n"; } // Wait for the caller connection thread to return connection result - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); sockaddr_in scl; int sclen = sizeof scl; @@ -343,15 +343,15 @@ TEST(CEPoll, NotifyConnectionBreak) this_thread::sleep_for(chrono::seconds(1)); cout << "(async call): Closing client connection\n"; return srt_close(client_sock); - }); + }); int timeout_ms = -1; - int ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; + SRTSOCKET ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK }; int len = 2; cout << "TEST: entering INFINITE WAIT\n"; const int epoll_wait_res = srt_epoll_wait(epoll_io, ready, &len, nullptr, nullptr, timeout_ms, 0, 0, 0, 0); cout << "TEST: return from INFINITE WAIT\n"; - if (epoll_wait_res == SRT_ERROR) + if (epoll_wait_res == int(SRT_ERROR)) cerr << "socket::read::epoll " << to_string(srt_getlasterror(nullptr)); EXPECT_EQ(epoll_wait_res, 1); EXPECT_EQ(len, 1); @@ -359,7 +359,7 @@ TEST(CEPoll, NotifyConnectionBreak) // Wait for the caller to close connection // There should be no wait, as epoll should wait until connection is closed. - EXPECT_EQ(close_res.get(), SRT_SUCCESS); + EXPECT_EQ(close_res.get(), SRT_STATUS_OK); const SRT_SOCKSTATUS state = srt_getsockstate(sock); const bool state_valid = state == SRTS_BROKEN || state == SRTS_CLOSING || state == SRTS_CLOSED; EXPECT_TRUE(state_valid); @@ -374,14 +374,14 @@ TEST(CEPoll, HandleEpollEvent2) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -435,14 +435,14 @@ TEST(CEPoll, HandleEpollNoEvent) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int yes = 1; const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); CEPoll epoll; const int epoll_id = epoll.create(); @@ -485,11 +485,11 @@ TEST(CEPoll, ThreadedUpdate) srt::TestInit srtinit; SRTSOCKET client_sock = srt_create_socket(); - EXPECT_NE(client_sock, SRT_ERROR); + EXPECT_NE(client_sock, SRT_INVALID_SOCK); const int no = 0; - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect CEPoll epoll; const int epoll_id = epoll.create(); @@ -550,8 +550,8 @@ class TestEPoll: public srt::Test { protected: - int m_client_pollid = SRT_ERROR; - SRTSOCKET m_client_sock = SRT_ERROR; + int m_client_pollid = int(SRT_ERROR); + SRTSOCKET m_client_sock = SRT_INVALID_SOCK; void clientSocket() { @@ -559,12 +559,12 @@ class TestEPoll: public srt::Test int no = 0; m_client_sock = srt_create_socket(); - ASSERT_NE(m_client_sock, SRT_ERROR); + ASSERT_NE(m_client_sock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); - ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_out = SRT_EPOLL_OUT; srt_epoll_add_usock(m_client_pollid, m_client_sock, &epoll_out); @@ -578,7 +578,7 @@ class TestEPoll: public srt::Test sockaddr* psa = (sockaddr*)&sa; - ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_ERROR); + ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_INVALID_SOCK); // Socket readiness for connection is checked by polling on WRITE allowed sockets. @@ -594,7 +594,7 @@ class TestEPoll: public srt::Test write, &wlen, -1, // -1 is set for debuging purpose. // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 0); // get exactly one write event without reads ASSERT_EQ(wlen, 1); // get exactly one write event without reads @@ -606,11 +606,11 @@ class TestEPoll: public srt::Test -1, // infinit ttl true // in order must be set to true ), - SRT_ERROR); + int(SRT_ERROR)); // disable receiving OUT events int epoll_err = SRT_EPOLL_ERR; - ASSERT_EQ(0, srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err)); + ASSERT_EQ(srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err), SRT_STATUS_OK); { int rlen = 2; SRTSOCKET read[2]; @@ -623,11 +623,11 @@ class TestEPoll: public srt::Test 1000, 0, 0, 0, 0)); const int last_error = srt_getlasterror(NULL); - EXPECT_EQ(SRT_ETIMEOUT, last_error) << last_error; + EXPECT_EQ(last_error, (int)SRT_ETIMEOUT) << last_error; } } - int m_server_pollid = SRT_ERROR; + int m_server_pollid = int(SRT_ERROR); void createServerSocket(SRTSOCKET& w_servsock) { @@ -635,10 +635,10 @@ class TestEPoll: public srt::Test int no = 0; SRTSOCKET servsock = srt_create_socket(); - ASSERT_NE(servsock, SRT_ERROR); + ASSERT_NE(servsock, SRT_INVALID_SOCK); - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + ASSERT_NE(srt_setsockflag(servsock, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + ASSERT_NE(srt_setsockflag(servsock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); int epoll_in = SRT_EPOLL_IN; srt_epoll_add_usock(m_server_pollid, servsock, &epoll_in); @@ -672,7 +672,7 @@ class TestEPoll: public srt::Test write, &wlen, -1, // -1 is set for debuging purpose. // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -699,7 +699,7 @@ class TestEPoll: public srt::Test write, &wlen, -1, // -1 is set for debuging purpose. // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + 0, 0, 0, 0), int(SRT_ERROR)); ASSERT_EQ(rlen, 1); // get exactly one read event without writes ASSERT_EQ(wlen, 0); // get exactly one read event without writes @@ -737,10 +737,10 @@ class TestEPoll: public srt::Test void setup() override { m_client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_client_pollid); + ASSERT_NE(m_client_pollid, int(SRT_ERROR)); m_server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, m_server_pollid); + ASSERT_NE(m_server_pollid, int(SRT_ERROR)); } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index ece753928..b0b8ac8a8 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -307,8 +307,8 @@ TEST(TestFEC, Connection) // Given 2s timeout for accepting as it has occasionally happened with Travis // that 1s might not be enough. SRTSOCKET a = srt_accept_bond(la, 1, 2000); - ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + ASSERT_NE(a, SRT_INVALID_SOCK); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -360,7 +360,7 @@ TEST(TestFEC, ConnectionReorder) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -412,7 +412,7 @@ TEST(TestFEC, ConnectionFull1) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -464,7 +464,7 @@ TEST(TestFEC, ConnectionFull2) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -516,7 +516,7 @@ TEST(TestFEC, ConnectionMess) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config @@ -566,7 +566,7 @@ TEST(TestFEC, ConnectionForced) SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + EXPECT_EQ(connect_res.get(), SRT_STATUS_OK); // Now that the connection is established, check negotiated config diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 97f9e684a..7f22df648 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -50,21 +50,22 @@ TEST(Transmission, FileUpload) // Find unused a port not used by any other service. // Otherwise srt_connect may actually connect. - int bind_res = -1; + SRTSTATUS bind_res = SRT_ERROR; for (int port = 5000; port <= 5555; ++port) { sa_lsn.sin_port = htons(port); bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); - if (bind_res == 0) + int bind_err = srt_getlasterror(NULL); + if (bind_res == SRT_STATUS_OK) { std::cout << "Running test on port " << port << "\n"; break; } - ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + ASSERT_TRUE(bind_err == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_err; } - ASSERT_GE(bind_res, 0); + ASSERT_GE((int)bind_res, 0); srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index ee11292b0..18fbf7595 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -30,12 +30,12 @@ class TestIPv6 void setup() override { m_caller_sock = srt_create_socket(); - ASSERT_NE(m_caller_sock, SRT_ERROR); + ASSERT_NE(m_caller_sock, SRT_INVALID_SOCK); // IPv6 calling IPv4 would otherwise fail if the system-default net.ipv6.bindv6only=1. ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_IPV6ONLY, &no, sizeof no), SRT_ERROR); m_listener_sock = srt_create_socket(); - ASSERT_NE(m_listener_sock, SRT_ERROR); + ASSERT_NE(m_listener_sock, SRT_INVALID_SOCK); } void teardown() override diff --git a/testing/srt-test-file.cpp b/testing/srt-test-file.cpp index 3dd8fe998..baacc66d1 100644 --- a/testing/srt-test-file.cpp +++ b/testing/srt-test-file.cpp @@ -261,7 +261,7 @@ bool DoUpload(UriParser& ut, string path, string filename) { int st = srt_send(ss, buf.data()+shift, int(n)); Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift)); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl; return false; @@ -290,7 +290,7 @@ bool DoUpload(UriParser& ut, string path, string filename) size_t bytes; size_t blocks; int st = srt_getsndbuffer(ss, &blocks, &bytes); - if (st == SRT_ERROR) + if (st == int(SRT_ERROR)) { cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl; return false; @@ -358,7 +358,7 @@ bool DoDownload(UriParser& us, string directory, string filename) for (;;) { int n = srt_recv(ss, buf.data(), int(::g_buffer_size)); - if (n == SRT_ERROR) + if (n == int(SRT_ERROR)) { cerr << "Download: SRT error: " << srt_getlasterror_str() << endl; return false; diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..bfccb3f1c 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -298,7 +298,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr { SRT_GROUP_TYPE gt; size = sizeof gt; - if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) + if (SRT_ERROR != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { if (gt < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; From 7a4e00ca2259935a8bf1e9785c7ae1baeeb73297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 10 Oct 2023 14:58:03 +0200 Subject: [PATCH 096/517] Fixed clang warnings (build break on some CI) --- apps/socketoptions.hpp | 4 ++-- testing/srt-test-live.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 12694f091..e6de4db17 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -58,7 +58,7 @@ struct SocketOption bool applyt(Object socket, std::string value) const; template - static int setso(Object socket, int protocol, int symbol, const void* data, size_t size) + static int setso(Object , int , int , const void* , size_t) { typename Object::wrong_version error; return -1; @@ -71,7 +71,7 @@ struct SocketOption template<> inline int SocketOption::setso(SRTSOCKET socket, int /*ignored*/, int sym, const void* data, size_t size) { - return (int)srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size); + return (int)srt_setsockflag(socket, SRT_SOCKOPT(sym), data, (int) size); } #if ENABLE_BONDING diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index bfccb3f1c..a68f81612 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (SRT_ERROR != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 6827df78cc22ccb8ea2ce75b2b45290e00488ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Oct 2023 15:11:02 +0200 Subject: [PATCH 097/517] Added tests for more cases and fixed remaining failures. STILL mutex not enabled. --- srtcore/api.cpp | 20 ++++++- srtcore/group.cpp | 1 + srtcore/group.h | 21 +++++-- test/test_epoll.cpp | 135 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 142 insertions(+), 35 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 9f592a07a..a1e1abb14 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -697,6 +697,12 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, } } + // This is set to true only if the group is to be reported + // from the accept call for the first time. Once it is extracted + // this way, this flag is cleared. + if (should_submit_to_accept) + g->m_bPending = true; + // Update the status in the group so that the next // operation can include the socket in the group operation. CUDTGroup::SocketData* gm = ns->m_GroupMemberData; @@ -850,8 +856,8 @@ SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() // matter is simple - if it's present, you light up the // SRT_EPOLL_ACCEPT flag. -//#if !ENABLE_BONDING -#if 1 +#if !ENABLE_BONDING +//#if 1 ScopedLock accept_lock (m_AcceptLock); // Make it simplified here - nonempty container = have acceptable sockets. @@ -891,7 +897,14 @@ int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) CUDTSocket* s = locateSocket_LOCKED(*i); if (!s) continue; // wiped in the meantime - ignore - if (s->m_GroupOf && s->m_GroupOf->groupConnected()) + + // If this pending socket is a group member, but the group + // to which it belongs is NOT waiting to be accepted, then + // light up the UPDATE event only. Light up ACCEPT only if + // this is a single socket, or this single socket has turned + // the mirror group to be first time available for accept(), + // and this accept() hasn't been done yet. + if (s->m_GroupOf && !s->m_GroupOf->groupPending()) flags |= SRT_EPOLL_UPDATE; else flags |= SRT_EPOLL_ACCEPT; @@ -1222,6 +1235,7 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int { u = s->m_GroupOf->m_GroupID; s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure + s->m_GroupOf->m_bPending = false; // Mark the beginning of the connection at the moment // when the group ID is returned to the app caller diff --git a/srtcore/group.cpp b/srtcore/group.cpp index c6b92a0ff..d70a32ba4 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -273,6 +273,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_RcvBaseSeqNo(SRT_SEQNO_NONE) , m_bOpened(false) , m_bConnected(false) + , m_bPending(false) , m_bClosing(false) , m_iLastSchedSeqNo(SRT_SEQNO_NONE) , m_iLastSchedMsgNo(SRT_MSGNO_NONE) diff --git a/srtcore/group.h b/srtcore/group.h index d31d9e610..90dcace16 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -203,9 +203,9 @@ class CUDTGroup return m_Group.empty(); } - bool groupConnected() + bool groupPending() { - return m_bConnected; + return m_bPending; } void setGroupConnected(); @@ -664,8 +664,21 @@ class CUDTGroup // from the first delivering socket will be taken as a good deal. sync::atomic m_RcvBaseSeqNo; - bool m_bOpened; // Set to true when at least one link is at least pending - bool m_bConnected; // Set to true on first link confirmed connected + /// True: at least one socket has joined the group in at least pending state + bool m_bOpened; + + /// True: at least one socket is connected, even if pending from the listener + bool m_bConnected; + + /// True: this group was created on the listner side for the first socket + /// that is pending connection, so the group is about to be reported for the + /// srt_accept() call, but the application hasn't retrieved the group yet. + /// Not in use in case of caller-side groups. + // NOTE: using atomic in otder to allow this variable to be changed independently + // on any mutex locks. + sync::atomic m_bPending; + + /// True: the group was requested to close and it should not allow any operations. bool m_bClosing; // There's no simple way of transforming config diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 99244642a..46cb10dce 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -545,80 +545,159 @@ TEST(CEPoll, ThreadedUpdate) } } -TEST(CEPoll, LateListenerReady) +void testListenerReady(const bool LATE_CALL, size_t nmembers) { - ASSERT_EQ(srt_startup(), 0); - - int server_sock = srt_create_socket(), caller_sock = srt_create_socket(); - sockaddr_in sa; memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_port = htons(5555); ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + TestInit init; + + SRTSOCKET server_sock, caller_sock; + server_sock = srt_create_socket(); + + if (nmembers > 0) + { + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + } + else + { + caller_sock = srt_create_socket(); + nmembers = 1; // Set to 1 so that caller starts at least once. + } + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); - srt_listen(server_sock, 1); + srt_listen(server_sock, nmembers+1); srt::setopt(server_sock)[SRTO_RCVSYN] = false; // Ok, the listener socket is ready; now make a call, but // do not do anything on the listener socket yet. -// This macro is to manipulate with the moment when the call is made -// towards the eid subscription. If 1, then the call is made first, -// and then subsciption after a 1s time. Set it to 0 to see how it -// works when the subscription is made first, so the readiness is from -// the listener changing the state. -#define LATE_CALL 1 + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; -#if LATE_CALL + std::vector> connect_res; - // We don't need the caller to be async, it can hang up here. - auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + if (LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } -#endif + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Sleeping 1s...\n"; this_thread::sleep_for(chrono::milliseconds(1000)); // What is important is that the accepted socket is now reporting in // on the listener socket. So let's create an epoll. int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); // and add this listener to it int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); -#if !LATE_CALL - - // We don't need the caller to be async, it can hang up here. - auto connect_res = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + if (!LATE_CALL) + { + // We don't need the caller to be async, it can hang up here. + for (size_t i = 0; i < nmembers; ++i) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + } -#endif + std::cout << "STARTED connecting...\n"; + } + std::cout << "Waiting for readiness...\n"; // And see now if the waiting accepted socket reports it. SRT_EPOLL_EVENT fdset[1]; EXPECT_EQ(srt_epoll_uwait(eid, fdset, 1, 5000), 1); + std::cout << "Accepting...\n"; sockaddr_in scl; int sclen = sizeof scl; SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen); EXPECT_NE(sock, SRT_INVALID_SOCK); - EXPECT_EQ(connect_res.get(), SRT_SUCCESS); + if (nmembers > 1) + { + std::cout << "With >1 members, check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + EXPECT_EQ(srt_epoll_uwait(eid_postcheck, fdset, 1, 5000), 1); + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + } + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < nmembers; ++i) + { + EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + } srt_epoll_release(eid); - srt_close(sock); + srt_epoll_release(eid_postcheck); + srt_close(server_sock); srt_close(caller_sock); + srt_close(sock); +} - EXPECT_EQ(srt_cleanup(), 0); +TEST(CEPoll, EarlyListenerReady) +{ + testListenerReady(false, 0); } +TEST(CEPoll, LateListenerReady) +{ + testListenerReady(true, 0); +} + +#if ENABLE_BONDING + +TEST(CEPoll, EarlyGroupListenerReady_1) +{ + testListenerReady(false, 1); +} + +TEST(CEPoll, LateGroupListenerReady_1) +{ + testListenerReady(true, 1); +} + +TEST(CEPoll, EarlyGroupListenerReady_3) +{ + testListenerReady(false, 3); +} + +TEST(CEPoll, LateGroupListenerReady_3) +{ + testListenerReady(true, 3); +} + +#endif class TestEPoll: public srt::Test { From 039291cda0aeca208f77e720f41a08c5aac265a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 11 Oct 2023 16:29:23 +0200 Subject: [PATCH 098/517] Fixed the problem with locking --- srtcore/api.cpp | 21 ++++++++++++--------- srtcore/api.h | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index a1e1abb14..cf524ea14 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -890,8 +890,6 @@ int srt::CUDTUnited::checkQueuedSocketsEvents(const set& sockets) // that they should re-read the group list and re-check readiness. // Now we can do lock once and for all - //ScopedLock glk (m_GlobControlLock); // XXX DEADLOCKS - for (set::iterator i = sockets.begin(); i != sockets.end(); ++i) { CUDTSocket* s = locateSocket_LOCKED(*i); @@ -2438,14 +2436,19 @@ int srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int } #endif - CUDTSocket* s = locateSocket(u); - if (s) - { - ret = epoll_add_usock_INTERNAL(eid, s, events); - } - else + // The call to epoll_add_usock_INTERNAL is expected + // to be called under m_GlobControlLock, so use this lock here, too. { - throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + ScopedLock cs (m_GlobControlLock); + CUDTSocket* s = locateSocket_LOCKED(u); + if (s) + { + ret = epoll_add_usock_INTERNAL(eid, s, events); + } + else + { + throw CUDTException(MJ_NOTSUP, MN_SIDINVAL); + } } return ret; diff --git a/srtcore/api.h b/srtcore/api.h index c049efde9..d08dca5b5 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -275,7 +275,10 @@ class CUDTUnited int& w_error, CUDT*& w_acpu); +#if ENABLE_BONDING + SRT_ATTR_REQUIRES(m_GlobControlLock) int checkQueuedSocketsEvents(const std::set& sockets); +#endif int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq); From bd9c4de9300ca9e8644a54f74596a8ed0a52ae0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 11:10:05 +0200 Subject: [PATCH 099/517] Fixed a problem: invalid per-listener report of member connections. Any pending connection can accept a group. Removed other pending sockets after accepting a group. Fixed tests to handle options also with RAII srt initializer --- docs/API/API-functions.md | 56 +++++++++-- srtcore/api.cpp | 110 +++++++++++++++++--- srtcore/api.h | 1 + srtcore/core.cpp | 3 +- srtcore/epoll.cpp | 25 ++++- srtcore/group.cpp | 20 ++++ srtcore/group.h | 16 ++- test/test_env.h | 7 +- test/test_epoll.cpp | 205 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 410 insertions(+), 33 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 90ed257dd..182ecdebc 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -652,16 +652,52 @@ the call will block until the incoming connection is ready. Otherwise, the call always returns immediately. The `SRT_EPOLL_IN` epoll event should be checked on the `lsn` socket prior to calling this function in that case. -If the pending connection is a group connection (initiated on the peer side by -calling the connection function using a group ID, and permitted on the listener -socket by the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) -flag), then the value returned is a group ID. This function then creates a new -group, as well as a new socket for this connection, that will be added to the -group. Once the group is created this way, further connections within the same -group, as well as sockets for them, will be created in the background. The -[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) event is raised on the `lsn` socket when -a new background connection is attached to the group, although it's usually for -internal use only. +Note that this event might sometimes be spurious in case when the link for +the pending connection gets broken before the accepting operation is finished. +The `SRT_EPOLL_IN` flag set for the listener socket is still not a guarantee +that the following call to `srt_accept` will succeed. + +If the listener socket is allowed to accept group connections, if it is set the +[`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag, then +a group ID will be returned, if it's a pending group connection. This can be +recognized by checking if `SRTGROUP_MASK` is set on the returned value. +Accepting a group connection differs to accepting a single socket connection +by that: + +1. The connection is reported only for the very first socket that has been +successfully connected. Only for this case will the `SRT_EPOLL_IN` flag be +set on the listener and only in this case will the `srt_accept` call report +the connection. + +2. Further member connections of the group that has been already once accepted +will be handled in the background, and the listener socket will no longer get +the `SRT_EPOLL_IN` flag set when it happens. Instead the +[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag will be set. This flag is +edge-triggered-only because there is no operation to be performed in response +that could make this flag cleared. It's mostly used internally and the +application may use it to update its group data cache. + +3. If your application has created more than one listener socket that has +allowed group connections, every newly connected socket that is a member of an +already connected group will join this group no matter to which listener +socket it was reported. If you want to use this feature, you should take special +care of how you perform the accept operation. The group connection may be +accepted off any of these listener sockets, but still only once. It is then +recommended: + + * In non-blocking mode, poll on all listener sockets that are expected to + get a group connection at once + + * In blocking mode, use `srt_accept_bond` instead (it uses epoll internally) + +Note also that in this case there are more chances for `SRT_EPOLL_IN` flag +to be spurious. For example, if you have two listener sockets configured for +group connection and on each of them there's a pending connection, you will +have `SRT_EPOLL_IN` flag set on both listener sockets. However, once you +accept the group connection on any of them, the other pending connection will +get automatically handled in the background and the existing `SRT_EPOLL_IN` +flag will be spurious (calling `srt_accept` on that listener socket will fail). + | Returns | | |:----------------------------- |:----------------------------------------------------------------------- | diff --git a/srtcore/api.cpp b/srtcore/api.cpp index cf524ea14..ad8bb154d 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -681,6 +681,17 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, goto ERR_ROLLBACK; } + // Acceptance of the group will have to be done through accepting + // of one of the pending sockets. There can be, however, multiple + // such sockets at a time, some of them might get broken before + // being accepted, and therefore we need to make all sockets ready. + // But then, acceptance of a group may happen only once, so if any + // sockets of the same group were submitted to accept, they must + // be removed from the accept queue at this time. + should_submit_to_accept = g->groupPending(); + + /* XXX remove if no longer informational + // Check if this is the first socket in the group. // If so, give it up to accept, otherwise just do nothing // The client will be informed about the newly added connection at the @@ -696,12 +707,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, break; } } - - // This is set to true only if the group is to be reported - // from the accept call for the first time. Once it is extracted - // this way, this flag is cleared. - if (should_submit_to_accept) - g->m_bPending = true; + */ // Update the status in the group so that the next // operation can include the socket in the group operation. @@ -714,11 +720,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, gm->rcvstate = SRT_GST_IDLE; gm->laststatus = SRTS_CONNECTED; - if (!g->m_bConnected) - { - HLOGC(cnlog.Debug, log << "newConnection(GROUP): First socket connected, SETTING GROUP CONNECTED"); - g->m_bConnected = true; - } + g->setGroupConnected(); // XXX PROLBEM!!! These events are subscribed here so that this is done once, lazily, // but groupwise connections could be accepted from multiple listeners for the same group! @@ -1231,13 +1233,16 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int // it's a theoretically possible scenario if (s->m_GroupOf) { - u = s->m_GroupOf->m_GroupID; - s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure - s->m_GroupOf->m_bPending = false; - + CUDTGroup* g = s->m_GroupOf; // Mark the beginning of the connection at the moment // when the group ID is returned to the app caller - s->m_GroupOf->m_stats.tsLastSampleTime = steady_clock::now(); + g->m_stats.tsLastSampleTime = steady_clock::now(); + + HLOGC(cnlog.Debug, log << "accept: reporting group $" << g->m_GroupID << " instead of member socket @" << u); + u = g->m_GroupID; + s->core().m_config.iGroupConnect = 1; // should be derived from ls, but make sure + g->m_bPending = false; + CUDT::uglobal().removePendingForGroup(g); } else { @@ -1263,6 +1268,79 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int return u; } +#if ENABLE_BONDING + +// [[using locked(m_GlobControlLock)]] +void srt::CUDTUnited::removePendingForGroup(const CUDTGroup* g) +{ + // We don't have a list of listener sockets that have ever + // reported a pending connection for a group, so the only + // way to find them is to ride over the list of all sockets... + + list members; + g->getMemberSockets((members)); + + for (sockets_t::iterator i = m_Sockets.begin(); i != m_Sockets.end(); ++i) + { + CUDTSocket* s = i->second; + // Check if any of them is a listener socket... + + /* XXX This is left for information only that we are only + interested with listener sockets - with the current + implementation checking it is pointless because the + m_QueuedSockets structure is present in every socket + anyway even if it's not a listener, and only listener + sockets may have this container nonempty. So checking + the container should suffice. + + if (!s->core().m_bListening) + continue; + */ + + if (s->m_QueuedSockets.empty()) + continue; + + // Somehow fortunate for us that it's a set, so we + // can simply check if this allegedly listener socket + // contains any of them. + for (list::iterator m = members.begin(), mx = m; m != members.end(); m = mx) + { + ++mx; + std::set::iterator q = s->m_QueuedSockets.find(*m); + if (q != s->m_QueuedSockets.end()) + { + HLOGC(cnlog.Debug, log << "accept: listener @" << s->m_SocketID + << " had ququed member @" << *m << " -- removed"); + // Found an intersection socket. + // Remove it from the listener queue + s->m_QueuedSockets.erase(q); + + // NOTE ALSO that after this removal the queue may be EMPTY, + // and if so, the listener socket should be no longer ready for accept. + if (s->m_QueuedSockets.empty()) + { + m_EPoll.update_events(s->m_SocketID, s->core().m_sPollID, SRT_EPOLL_ACCEPT, false); + } + + // and remove it also from the members list. + // This can be done safely because we use a SAFE LOOP. + // We can also do it safely because a socket may be + // present in only one listener socket in the whole app. + members.erase(m); + } + } + + // It may happen that the list of members can be + // eventually purged even if we haven't checked every socket. + // If it happens so, quit immediately because there's nothing + // left to do. + if (members.empty()) + return; + } +} + +#endif + int srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int namelen) { // Here both srcname and tarname must be specified diff --git a/srtcore/api.h b/srtcore/api.h index d08dca5b5..412375688 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -278,6 +278,7 @@ class CUDTUnited #if ENABLE_BONDING SRT_ATTR_REQUIRES(m_GlobControlLock) int checkQueuedSocketsEvents(const std::set& sockets); + void removePendingForGroup(const CUDTGroup* g); #endif int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index fe30efa2b..c6b50292c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3300,7 +3300,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 // This can only happen on a listener (it's only called on a site that is // HSD_RESPONDER), so it was a response for a groupwise connection. // Therefore such a group shall always be considered opened. - gp->setOpen(); + // It's also set pending and it stays this way until accepted. + gp->setOpenPending(); HLOGC(gmlog.Debug, log << CONID() << "makeMePeerOf: no group has peer=$" << peergroup << " - creating new mirror group $" diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index e2b861bf9..80fe53a65 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -506,18 +506,23 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ScopedLock pg(m_EPollLock); map::iterator p = m_mPolls.find(eid); if (p == m_mPolls.end()) + { + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " doesn't exist"); throw CUDTException(MJ_NOTSUP, MN_EIDINVAL); + } CEPollDesc& ed = p->second; if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty()) { // Empty EID is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is empty (use SRT_EPOLL_ENABLE_EMPTY to allow)"); throw CUDTException(MJ_NOTSUP, MN_EEMPTY); } if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0)) { - // Empty EID is not allowed, report error. + // Empty container is not allowed, report error. + LOGC(ealog.Error, log << "epoll_uwait: empty output container with E" << eid << " (use SRT_EPOLL_ENABLE_OUTPUTCHECK to allow)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -525,6 +530,7 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 { // XXX Add error log // uwait should not be used with EIDs subscribed to system sockets + LOGC(ealog.Error, log << "epoll_uwait: E" << eid << " is subscribed to system sckets (not allowed for uwait)"); throw CUDTException(MJ_NOTSUP, MN_INVAL); } @@ -536,11 +542,20 @@ int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int6 ++total; if (total > fdsSize) + { + HLOGC(ealog.Debug, log << "epoll_uwait: output container size=" << fdsSize << " insufficient to report all sockets"); break; + } fdsSet[pos] = *i; + IF_HEAVY_LOGGING(std::ostringstream out); + IF_HEAVY_LOGGING(out << "epoll_uwait: Notice: fd=" << i->fd << " events="); + IF_HEAVY_LOGGING(PrintEpollEvent(out, i->events, 0)); + + SRT_ATR_UNUSED const bool was_edge = ed.checkEdge(i++); // NOTE: potentially deletes `i` + IF_HEAVY_LOGGING(out << (was_edge ? "(^)" : "")); + HLOGP(ealog.Debug, out.str()); - ed.checkEdge(i++); // NOTE: potentially deletes `i` } if (total) return total; @@ -875,6 +890,12 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const return -1; // still, ignored. } + if (uid == SRT_INVALID_SOCK || uid == 0) + { + LOGC(eilog.Fatal, log << "epoll/update: IPE: invalid 'uid' submitted for update!"); + return -1; + } + int nupdated = 0; vector lost; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index d70a32ba4..f3931d1d6 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -3944,6 +3944,7 @@ void CUDTGroup::setGroupConnected() { if (!m_bConnected) { + HLOGC(cnlog.Debug, log << "GROUP: First socket connected, SETTING GROUP CONNECTED"); // Switch to connected state and give appropriate signal m_Global.m_EPoll.update_events(id(), m_sPollID, SRT_EPOLL_CONNECT, true); m_bConnected = true; @@ -4016,6 +4017,25 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } } +struct FExtractGroupID +{ + SRTSOCKET operator()(const groups::SocketData& d) + { + return d.id; + } +}; + +void CUDTGroup::getMemberSockets(std::list& w_ids) const +{ + ScopedLock gl (m_GroupLock); + + for (cgli_t gi = m_Group.begin(); gi != m_Group.end(); ++gi) + { + w_ids.push_back(gi->id); + } +} + + void CUDTGroup::activateUpdateEvent(bool still_have_items) { // This function actually reacts on the fact that a socket diff --git a/srtcore/group.h b/srtcore/group.h index 90dcace16..3ee51bb47 100644 --- a/srtcore/group.h +++ b/srtcore/group.h @@ -101,6 +101,7 @@ class CUDTGroup typedef std::list group_t; typedef group_t::iterator gli_t; + typedef group_t::const_iterator cgli_t; typedef std::vector< std::pair > sendable_t; struct Sendstate @@ -402,7 +403,7 @@ class CUDTGroup void getGroupCount(size_t& w_size, bool& w_still_alive); srt::CUDTUnited& m_Global; - srt::sync::Mutex m_GroupLock; + mutable srt::sync::Mutex m_GroupLock; SRTSOCKET m_GroupID; SRTSOCKET m_PeerGroupID; @@ -431,6 +432,8 @@ class CUDTGroup gli_t begin() { return m_List.begin(); } gli_t end() { return m_List.end(); } + cgli_t begin() const { return m_List.begin(); } + cgli_t end() const { return m_List.end(); } bool empty() { return m_List.empty(); } void push_back(const SocketData& data) { m_List.push_back(data); ++m_SizeCache; } void clear() @@ -752,7 +755,14 @@ class CUDTGroup // Required after the call on newGroup on the listener side. // On the listener side the group is lazily created just before // accepting a new socket and therefore always open. - void setOpen() { m_bOpened = true; } + // However, after creation it will be still waiting for being + // extracted by the application in `srt_accept`, and until then + // it stays as pending. + void setOpenPending() + { + m_bOpened = true; + m_bPending = true; + } std::string CONID() const { @@ -812,6 +822,8 @@ class CUDTGroup void updateLatestRcv(srt::CUDTSocket*); + void getMemberSockets(std::list&) const; + // Property accessors SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID); SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, peerid, m_PeerGroupID); diff --git a/test/test_env.h b/test/test_env.h index 2e2f78f7d..2b26995dd 100644 --- a/test/test_env.h +++ b/test/test_env.h @@ -53,7 +53,11 @@ class TestInit static void start(int& w_retstatus); static void stop(); - TestInit() { start((ninst)); } + TestInit() + { + start((ninst)); + HandlePerTestOptions(); + } ~TestInit() { stop(); } void HandlePerTestOptions(); @@ -71,7 +75,6 @@ class Test: public testing::Test void SetUp() override final { init_holder.reset(new TestInit); - init_holder->HandlePerTestOptions(); setup(); } diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 46cb10dce..ec3cf7fd5 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,6 +12,36 @@ using namespace std; using namespace srt; +static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) +{ + static pair const namemap [] = { + make_pair(SRT_EPOLL_IN, "R"), + make_pair(SRT_EPOLL_OUT, "W"), + make_pair(SRT_EPOLL_ERR, "E"), + make_pair(SRT_EPOLL_UPDATE, "U") + }; + bool any = false; + + const int N = (int)Size(namemap); + + for (int i = 0; i < N; ++i) + { + if (events & namemap[i].first) + { + os << "["; + if (et_events & namemap[i].first) + os << "^"; + os << namemap[i].second << "]"; + any = true; + } + } + + if (!any) + os << "[]"; + + return os; +} + TEST(CEPoll, InfiniteWait) { @@ -697,8 +727,183 @@ TEST(CEPoll, LateGroupListenerReady_3) testListenerReady(true, 3); } + +void testMultipleListenerReady(const bool LATE_CALL) +{ + sockaddr_in sa; + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(5555); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + + sockaddr_in sa2; + memset(&sa2, 0, sizeof sa2); + sa2.sin_family = AF_INET; + sa2.sin_port = htons(5556); + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa2.sin_addr), 1); + + TestInit init; + + SRTSOCKET server_sock, server_sock2, caller_sock; + server_sock = srt_create_socket(); + server_sock2 = srt_create_socket(); + + caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); + int on = 1; + EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + EXPECT_NE(srt_setsockflag(server_sock2, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + + srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa)); + srt_listen(server_sock, 3); + srt::setopt(server_sock)[SRTO_RCVSYN] = false; + + srt_bind(server_sock2, (sockaddr*)& sa2, sizeof(sa2)); + srt_listen(server_sock2, 3); + srt::setopt(server_sock2)[SRTO_RCVSYN] = false; + + // Ok, the listener socket is ready; now make a call, but + // do not do anything on the listener socket yet. + + std::cout << "Using " << (LATE_CALL ? "LATE" : "EARLY") << " call\n"; + + std::vector> connect_res; + + if (LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + })); + + + std::cout << "STARTED connecting...\n"; + } + + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + + // What is important is that the accepted socket is now reporting in + // on the listener socket. So let's create an epoll. + + int eid = srt_epoll_create(); + int eid_postcheck = srt_epoll_create(); + + // and add this listener to it + int modes = SRT_EPOLL_IN; + int modes_postcheck = SRT_EPOLL_IN | SRT_EPOLL_UPDATE; + EXPECT_NE(srt_epoll_add_usock(eid, server_sock, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid, server_sock2, &modes), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock, &modes_postcheck), SRT_ERROR); + EXPECT_NE(srt_epoll_add_usock(eid_postcheck, server_sock2, &modes_postcheck), SRT_ERROR); + + if (!LATE_CALL) + { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + })); + + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { + this_thread::sleep_for(chrono::milliseconds(1)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + })); + + std::cout << "STARTED connecting...\n"; + } + + // Sleep to make sure that the connection process has started. + this_thread::sleep_for(chrono::milliseconds(100)); + + std::cout << "Waiting for readiness on @" << server_sock << " and @" << server_sock2 << "\n"; + // And see now if the waiting accepted socket reports it. + + // This time we should expect that the connection reports in + // on two listener sockets + SRT_EPOLL_EVENT fdset[2] = {}; + std::ostringstream out; + + int nready = srt_epoll_uwait(eid, fdset, 2, 5000); + EXPECT_EQ(nready, 2); + out << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + out << " @" << fdset[i].fd; + PrintEpollEvent(out, fdset[i].events); + } + out << std::endl; + std::cout << out.str(); + + std::cout << "Accepting...\n"; + sockaddr_in scl; + int sclen = sizeof scl; + + // We choose the SECOND one to extract the group connection. + SRTSOCKET sock = srt_accept(server_sock2, (sockaddr*)& scl, &sclen); + EXPECT_NE(sock, SRT_INVALID_SOCK); + + // Make sure this time that the accepted connection is a group. + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + + std::cout << "Check if there's still UPDATE pending\n"; + // Spawn yet another connection within the group, just to get the update + auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); + // For 2+ members, additionally check if there AREN'T any + // further acceptance members, but there are UPDATEs. + // Note that if this was done AFTER accepting, the UPDATE would + // be only set one one socket. + nready = srt_epoll_uwait(eid_postcheck, fdset, 1, 5000); + EXPECT_EQ(nready, 1); + + std::cout << "Ready socks:"; + for (int i = 0; i < nready; ++i) + { + std::cout << " @" << fdset[i].fd; + PrintEpollEvent(std::cout, fdset[i].events); + } + std::cout << std::endl; + + // SUBSCRIBED EVENTS: IN, UPDATE. + // expected: UPDATE only. + EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); + EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + + std::cout << "Joining connector thread(s)\n"; + for (size_t i = 0; i < connect_res.size(); ++i) + { + EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + } + + srt_epoll_release(eid); + srt_epoll_release(eid_postcheck); + + srt_close(server_sock); + srt_close(server_sock2); + srt_close(caller_sock); + srt_close(sock); +} + +TEST(CEPoll, EarlyGroupMultiListenerReady) +{ + testMultipleListenerReady(false); +} + +TEST(CEPoll, LateGroupMultiListenerReady) +{ + testMultipleListenerReady(true); +} + + + #endif + class TestEPoll: public srt::Test { protected: From 8d3c5ca795af950ff5bd492aff6bc647232ff9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 16:08:36 +0200 Subject: [PATCH 100/517] Updated documentation --- docs/API/API-functions.md | 166 ++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 68 deletions(-) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 182ecdebc..6269c4373 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -627,76 +627,106 @@ the listener socket to accept group connections SRTSOCKET srt_accept(SRTSOCKET lsn, struct sockaddr* addr, int* addrlen); ``` -Accepts a pending connection, then creates and returns a new socket or -group ID that handles this connection. The group and socket can be -distinguished by checking the `SRTGROUP_MASK` bit on the returned ID. - -* `lsn`: the listener socket previously configured by [`srt_listen`](#srt_listen) -* `addr`: the IP address and port specification for the remote party +Extracts the first connection request on the queue of pending connections for +the listening socket, `lsn`, then creates and returns a new socket or group ID +that handles this connection. The group and socket can be distinguished by +checking the `SRTGROUP_MASK` bit on the returned ID. Note that by default group +connections will be rejected - this feature can be only enabled on demand (see +below). + +* `lsn`: the listening socket +* `addr`: a location to store the remote IP address and port for the connection * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of the returned object -**NOTE:** `addr` is allowed to be NULL, in which case it's understood that the -application is not interested in the address from which the connection originated. -Otherwise `addr` should specify an object into which the address will be written, -and `addrlen` must also specify a variable to contain the object size. Note also -that in the case of group connection only the initial connection that -establishes the group connection is returned, together with its address. As -member connections are added or broken within the group, you can obtain this -information through [`srt_group_data`](#srt_group_data) or the data filled by -[`srt_sendmsg2`](#srt_sendmsg) and [`srt_recvmsg2`](#srt_recvmsg2). - -If the `lsn` listener socket is configured for blocking mode -([`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) set to true, default), -the call will block until the incoming connection is ready. Otherwise, the -call always returns immediately. The `SRT_EPOLL_IN` epoll event should be -checked on the `lsn` socket prior to calling this function in that case. - -Note that this event might sometimes be spurious in case when the link for -the pending connection gets broken before the accepting operation is finished. -The `SRT_EPOLL_IN` flag set for the listener socket is still not a guarantee -that the following call to `srt_accept` will succeed. - -If the listener socket is allowed to accept group connections, if it is set the -[`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) flag, then -a group ID will be returned, if it's a pending group connection. This can be -recognized by checking if `SRTGROUP_MASK` is set on the returned value. -Accepting a group connection differs to accepting a single socket connection -by that: - -1. The connection is reported only for the very first socket that has been -successfully connected. Only for this case will the `SRT_EPOLL_IN` flag be -set on the listener and only in this case will the `srt_accept` call report -the connection. - -2. Further member connections of the group that has been already once accepted -will be handled in the background, and the listener socket will no longer get -the `SRT_EPOLL_IN` flag set when it happens. Instead the -[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag will be set. This flag is -edge-triggered-only because there is no operation to be performed in response -that could make this flag cleared. It's mostly used internally and the -application may use it to update its group data cache. - -3. If your application has created more than one listener socket that has -allowed group connections, every newly connected socket that is a member of an -already connected group will join this group no matter to which listener -socket it was reported. If you want to use this feature, you should take special -care of how you perform the accept operation. The group connection may be -accepted off any of these listener sockets, but still only once. It is then -recommended: - - * In non-blocking mode, poll on all listener sockets that are expected to - get a group connection at once - - * In blocking mode, use `srt_accept_bond` instead (it uses epoll internally) - -Note also that in this case there are more chances for `SRT_EPOLL_IN` flag -to be spurious. For example, if you have two listener sockets configured for -group connection and on each of them there's a pending connection, you will -have `SRT_EPOLL_IN` flag set on both listener sockets. However, once you -accept the group connection on any of them, the other pending connection will -get automatically handled in the background and the existing `SRT_EPOLL_IN` -flag will be spurious (calling `srt_accept` on that listener socket will fail). +General requirements for parameter correctness: + +* `lsn` must be first [bound](#srt_bind) and [listening](#srt_listen) + +* `addr` may be NULL, or otherwise it must be a pointer to an object +that can be treated as an instance of `sockaddr_in` or `sockaddr_in6` + +* `addrlen` should be a pointer to a variable set to the size of the object +specified in `addr`, if `addr` is not NULL. Otherwise it's ignored. + +If `addr` is not NULL, the information about the source IP address and +port of the peer will be written into this object. Note that whichever +type of object is expected here (`sockaddr_in` or `sockaddr_in6`), it +depends on the address type used in the `srt_bind` call for `lsn`. +If unsure in a particular situation, it is recommended that you use +`sockaddr_storage` or `srt::sockaddr_any`. + +If the `lsn` listener socket is blocking mode (if +[`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, +which is default), the call will block until the incoming connection is ready +for extraction. Otherwise, the call always returns immediately, possibly with +failure, if there was no pending connection waiting on the listening socket +`lsn`. + +The listener socket can be checked for any pending connections prior to calling +`srt_accept` by checking the `SRT_EPOLL_ACCEPT` epoll event (which is an alias +to `SRT_EPOLL_IN`). This event might be spurious in certain cases though, for +example, when the connection has been closed by the peer or broken before the +application extracts it. The call to `srt_accept` would then still fail in +such a case. + +In order to allow the listening socket `lsn` to accept a group connection, +the [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) socket option +for the listening socket must be set to 1. Note that single socket connections +can still be reported to that socket. The application can distinguish the socket +and group connection by checking the `SRTGROUP_MASK` bit on the returned +successful value. There are some important differences to single socket +connections: + +1. Accepting a group connection can be done only once per connection. The +actual connection reporter is a socket, like before, but once you call +`srt_accept` and receive this group ID, it is the group considered connected, +and any other member connections of the same group will be handled in the +background. + +2. If a group was extracted from the `srt_accept` call, the address reported in +`addr` parameter is still the address of the connection that has triggered the +group connection extraction. While the group is connected, potentially new +connections may be added and any existing ones get broken at any time. The +information about all member connections, that are active at the moment, can be +obtained at any time through [`srt_group_data`](#srt_group_data) or the data +filled by [`srt_sendmsg2`](#srt_sendmsg2) and [`srt_recvmsg2`](#srt_recvmsg2) +in the [`SRT_MSGCTRL`](#SRT_MSGCTRL) structure. + +3. Listening sockets are not bound to groups anyhow. You can allow multiple +listening sockets to accept group connections and the connection extracted +from the listener, if it is declared to be a group member, will join its +group, no matter which of the listening sockets has received the connection +request. This feature is prone to more tricky rules, however: + + * If you use multiple listener sockets, all of them in blocking mode, + allowed for group connections, and receiving connection requests for + the same group at the moment, and you run one thread per `srt_accept` + call, it is undefined, which of them will extract the group ID + for the connection, but still only one will, while the others will + continue blocking. If you want to use only one thread for accepting + connections from potentially multiple listening sockets in the blocking + mode, you should use [`srt_accept_bond`](#srt_accept_bond) instead. + + * If at the moment multiple listener sockets have received connection + request and you query them all for readiness epoll flags (by calling + an epoll waiting function), all of them will get the `SRT_EPOLL_ACCEPT` + flag set, but still only one of them will return the group ID from the + `srt_accept` call. After this call, from all listener sockets in the + whole application the `SRT_EPOLL_ACCEPT` flag, that was set by the reason + of a pending connection for the same group, will be withdrawn (that is, + it will be cleared if there are no other pending connections). This is + then yet another situation when this flag can be spurious. + +4. If you query a listening socket for epoll flags after the `srt_accept` +function has once returned the group ID, the listening sockets that have +received new member connection requests within that group will report only the +[`SRT_EPOLL_UPDATE`](#SRT_EPOLL_UPDATE) flag. This flag is edge-triggered-only +because there is no operation you can perform in response in order to clear +this flag. This flag is mostly used internally and the application may use it +if it would like to trigger updating the current group information due to +having one newly added member connection. + | Returns | | @@ -707,7 +737,7 @@ flag will be spurious (calling `srt_accept` on that listener socket will fail). | Errors | | |:--------------------------------- |:----------------------------------------------------------------------- | -| [`SRT_EINVPARAM`](#srt_einvparam) | NULL specified as `addrlen`, when `addr` is not NULL | +| [`SRT_EINVPARAM`](#srt_einvparam) | Invalid `addr` or `addrlen` (see requirements in the begininng) | | [`SRT_EINVSOCK`](#srt_einvsock) | `lsn` designates no valid socket ID. | | [`SRT_ENOLISTEN`](#srt_enolisten) | `lsn` is not set up as a listener ([`srt_listen`](#srt_listen) not called). | | [`SRT_EASYNCRCV`](#srt_easyncrcv) | No connection reported so far. This error is reported only in the non-blocking mode | From ab96b4d6a7cb2debb9710365ef2ba5b44025b937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 13 Oct 2023 17:37:25 +0200 Subject: [PATCH 101/517] Weird warning/error on Mac --- test/test_epoll.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index ec3cf7fd5..0d4e29a67 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,7 +12,8 @@ using namespace std; using namespace srt; -static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) +namespace { +ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) { static pair const namemap [] = { make_pair(SRT_EPOLL_IN, "R"), @@ -41,6 +42,7 @@ static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) return os; } +} TEST(CEPoll, InfiniteWait) From 9b93a3a1ef36509632302b1d2cc8048009543ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 16 Oct 2023 11:26:06 +0200 Subject: [PATCH 102/517] Some cosmetic fixes. Fixes for CI build breaks --- srtcore/api.cpp | 1 - srtcore/common.cpp | 48 +++++++++++++++++++++++++++++++++++++++ srtcore/common.h | 19 ++-------------- srtcore/core.cpp | 2 -- srtcore/epoll.cpp | 31 ------------------------- srtcore/group.cpp | 9 -------- test/test_epoll.cpp | 31 ------------------------- testing/srt-test-live.cpp | 2 +- 8 files changed, 51 insertions(+), 92 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index ad8bb154d..bbc7be2c9 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -859,7 +859,6 @@ SRT_EPOLL_T srt::CUDTSocket::getListenerEvents() // SRT_EPOLL_ACCEPT flag. #if !ENABLE_BONDING -//#if 1 ScopedLock accept_lock (m_AcceptLock); // Make it simplified here - nonempty container = have acceptable sockets. diff --git a/srtcore/common.cpp b/srtcore/common.cpp index dc60b3db7..61ea56311 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -463,6 +463,54 @@ bool SrtParseConfig(const string& s, SrtConfig& w_config) return true; } + +std::string FormatLossArray(const std::vector< std::pair >& lra) +{ + std::ostringstream os; + + os << "[ "; + for (std::vector< std::pair >::const_iterator i = lra.begin(); i != lra.end(); ++i) + { + int len = CSeqNo::seqoff(i->first, i->second); + os << "%" << i->first; + if (len > 1) + os << "+" << len; + os << " "; + } + + os << "]"; + return os.str(); +} + +ostream& PrintEpollEvent(ostream& os, int events, int et_events) +{ + static pair const namemap [] = { + make_pair(SRT_EPOLL_IN, "R"), + make_pair(SRT_EPOLL_OUT, "W"), + make_pair(SRT_EPOLL_ERR, "E"), + make_pair(SRT_EPOLL_UPDATE, "U") + }; + bool any = false; + + const int N = (int)Size(namemap); + + for (int i = 0; i < N; ++i) + { + if (events & namemap[i].first) + { + os << "["; + if (et_events & namemap[i].first) + os << "^"; + os << namemap[i].second << "]"; + any = true; + } + } + + if (!any) + os << "[]"; + + return os; +} } // namespace srt namespace srt_logging diff --git a/srtcore/common.h b/srtcore/common.h index bfc0c1d96..276e47c7c 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -1430,23 +1430,8 @@ inline bool checkMappedIPv4(const sockaddr_in6& sa) return checkMappedIPv4(addr); } -inline std::string FormatLossArray(const std::vector< std::pair >& lra) -{ - std::ostringstream os; - - os << "[ "; - for (std::vector< std::pair >::const_iterator i = lra.begin(); i != lra.end(); ++i) - { - int len = CSeqNo::seqoff(i->first, i->second); - os << "%" << i->first; - if (len > 1) - os << "+" << len; - os << " "; - } - - os << "]"; - return os.str(); -} +std::string FormatLossArray(const std::vector< std::pair >& lra); +std::ostream& PrintEpollEvent(std::ostream& os, int events, int et_events = 0); } // namespace srt diff --git a/srtcore/core.cpp b/srtcore/core.cpp index c6b50292c..3ec9554b4 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -11676,7 +11676,6 @@ void srt::CUDT::addEPoll(const int eid) m_sPollID.insert(eid); leaveCS(uglobal().m_EPoll.m_EPollLock); - //* if (m_bListening) { // A listener socket can only get readiness on SRT_EPOLL_ACCEPT @@ -11694,7 +11693,6 @@ void srt::CUDT::addEPoll(const int eid) // used for listening and nothing else. return; } - // */ if (!stillConnected()) return; diff --git a/srtcore/epoll.cpp b/srtcore/epoll.cpp index 80fe53a65..e6ef44897 100644 --- a/srtcore/epoll.cpp +++ b/srtcore/epoll.cpp @@ -71,12 +71,6 @@ modified by using namespace std; using namespace srt::sync; -#if ENABLE_HEAVY_LOGGING -namespace srt { -static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0); -} -#endif - namespace srt_logging { extern Logger eilog, ealog; @@ -977,31 +971,6 @@ int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set& eids, const namespace srt { -static ostream& PrintEpollEvent(ostream& os, int events, int et_events) -{ - static pair const namemap [] = { - make_pair(SRT_EPOLL_IN, "R"), - make_pair(SRT_EPOLL_OUT, "W"), - make_pair(SRT_EPOLL_ERR, "E"), - make_pair(SRT_EPOLL_UPDATE, "U") - }; - - const int N = (int)Size(namemap); - - for (int i = 0; i < N; ++i) - { - if (events & namemap[i].first) - { - os << "["; - if (et_events & namemap[i].first) - os << "^"; - os << namemap[i].second << "]"; - } - } - - return os; -} - string DisplayEpollResults(const std::map& sockset) { typedef map fmap_t; diff --git a/srtcore/group.cpp b/srtcore/group.cpp index f3931d1d6..4704a66e5 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -4017,14 +4017,6 @@ void CUDTGroup::updateLatestRcv(CUDTSocket* s) } } -struct FExtractGroupID -{ - SRTSOCKET operator()(const groups::SocketData& d) - { - return d.id; - } -}; - void CUDTGroup::getMemberSockets(std::list& w_ids) const { ScopedLock gl (m_GroupLock); @@ -4035,7 +4027,6 @@ void CUDTGroup::getMemberSockets(std::list& w_ids) const } } - void CUDTGroup::activateUpdateEvent(bool still_have_items) { // This function actually reacts on the fact that a socket diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 0d4e29a67..38624a62e 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -12,37 +12,6 @@ using namespace std; using namespace srt; -namespace { -ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0) -{ - static pair const namemap [] = { - make_pair(SRT_EPOLL_IN, "R"), - make_pair(SRT_EPOLL_OUT, "W"), - make_pair(SRT_EPOLL_ERR, "E"), - make_pair(SRT_EPOLL_UPDATE, "U") - }; - bool any = false; - - const int N = (int)Size(namemap); - - for (int i = 0; i < N; ++i) - { - if (events & namemap[i].first) - { - os << "["; - if (et_events & namemap[i].first) - os << "^"; - os << namemap[i].second << "]"; - any = true; - } - } - - if (!any) - os << "[]"; - - return os; -} -} TEST(CEPoll, InfiniteWait) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..aa6b45d6a 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 33de3eb164f852c27ff2822089c0108631609d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 16 Oct 2023 12:57:52 +0200 Subject: [PATCH 103/517] Fixed rejection code naming --- srtcore/core.cpp | 4 ++-- srtcore/srt.h | 4 ++-- test/test_ipv6.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e3c29b651..22876663f 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4711,7 +4711,7 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " << m_config.zExpPayloadSize); - m_RejectReason = SRT_REJ_SETTINGS; + m_RejectReason = SRT_REJ_CONFIG; return false; } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -5733,7 +5733,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " << m_config.zExpPayloadSize); - m_RejectReason = SRT_REJ_SETTINGS; + m_RejectReason = SRT_REJ_CONFIG; throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } diff --git a/srtcore/srt.h b/srtcore/srt.h index c5bd4ed5e..c29e2c506 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -569,11 +569,11 @@ enum SRT_REJECT_REASON SRT_REJ_CONGESTION, // incompatible congestion-controller type SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group - SRT_REJ_TIMEOUT, // connection timeout + SRT_REJ_TIMEOUT = 16,// connection timeout #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif - SRT_REJ_SETTINGS, // socket settings on both sides collide and can't be negotiated + SRT_REJ_CONFIG = 18, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index e8c14141d..9843a0303 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -117,7 +117,7 @@ class TestIPv6 { // Version with expected failure EXPECT_EQ(connect_res, SRT_ERROR); - EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_SETTINGS); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_CONFIG); srt_close(m_listener_sock); } std::cout << "Connect: exit\n"; From dc7f4372de47e328a3f68d88c8a061a1218ea6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 17 Oct 2023 14:56:29 +0200 Subject: [PATCH 104/517] Added an API function to control the payload size --- apps/transmitmedia.cpp | 57 +++++++++++++++++++++++++++++++++------- apps/transmitmedia.hpp | 1 + srtcore/api.cpp | 41 +++++++++++++++++++++++++++++ srtcore/api.h | 2 ++ srtcore/core.h | 1 + srtcore/socketconfig.cpp | 50 ++++++++++++++++++++++------------- srtcore/socketconfig.h | 2 ++ srtcore/srt.h | 6 ++--- srtcore/srt_c_api.cpp | 26 ++++++++++++------ testing/testmedia.cpp | 57 +++++++++++++++++++++++++++++++++------- testing/testmedia.hpp | 1 + 11 files changed, 195 insertions(+), 49 deletions(-) diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 9c9e88401..27bdda22b 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -179,7 +179,7 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } - int fam_to_limit_size = AF_INET6; // take the less one as default + unsigned int max_payload_size = 0; // Try to interpret host and adapter first sockaddr_any host_sa, adapter_sa; @@ -187,27 +187,31 @@ void SrtCommon::InitParameters(string host, map par) if (host != "") { host_sa = CreateAddr(host); - fam_to_limit_size = host_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (host_sa.family() == AF_UNSPEC) Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } if (adapter != "" && adapter != host) { adapter_sa = CreateAddr(adapter); - fam_to_limit_size = adapter_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (adapter_sa.family() == AF_UNSPEC) Error("Failed to interpret 'adapter' spec: " + adapter); if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) { Error("Both host and adapter specified and they use different IP versions"); } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } - if (fam_to_limit_size != AF_INET) - fam_to_limit_size = AF_INET6; + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { @@ -225,12 +229,17 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); - if (transmit_chunk_size > size_limit) - throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); + if (transmit_chunk_size > max_payload_size) + Error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assign the others here. m_options = par; @@ -294,6 +303,21 @@ bool SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + // we do one client connection at a time, // so close the listener. srt_close(m_bindsock); @@ -462,6 +486,19 @@ void SrtCommon::ConnectClient(string host, int port) Error("srt_connect"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + stat = ConfigurePost(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePost"); diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 527f005d9..a4812d815 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -42,6 +42,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; SRTSOCKET m_sock = SRT_INVALID_SOCK; SRTSOCKET m_bindsock = SRT_INVALID_SOCK; bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; } diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 5646e3e79..371bf1b0a 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4331,6 +4331,47 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) } } +int srt::CUDT::getMaxPayloadSize(SRTSOCKET id) +{ + return uglobal().getMaxPayloadSize(id); +} + +int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) +{ + CUDTSocket* s = locateSocket(id); + if (!s) + { + return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL); + } + + if (s->m_SelfAddr.family() == AF_UNSPEC) + { + return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND); + } + + int fam = s->m_SelfAddr.family(); + CUDT& u = s->core(); + + std::string errmsg; + int extra = u.m_config.extraPayloadReserve((errmsg)); + if (extra == -1) + { + LOGP(aclog.Error, errmsg); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL); + } + + // Prefer transfer IP version, if defined. This is defined after + // the connection is established. Note that the call is rejected + // if the socket isn't bound, be it explicitly or implicitly by + // calling srt_connect(). + if (u.m_TransferIPVersion != AF_UNSPEC) + fam = u.m_TransferIPVersion; + + int payload_size = u.m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(fam) - extra; + + return payload_size; +} + //////////////////////////////////////////////////////////////////////////////// namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..6a5783044 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -412,6 +412,8 @@ class CUDTUnited CUDTSocket* locateSocket_LOCKED(SRTSOCKET u); CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn); + int getMaxPayloadSize(SRTSOCKET u); + #if ENABLE_BONDING CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); CUDTGroup* acquireSocketsGroup(CUDTSocket* s); diff --git a/srtcore/core.h b/srtcore/core.h index 6945e4276..4d3a84f42 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -245,6 +245,7 @@ class CUDT static int rejectReason(SRTSOCKET s); static int rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); + static int getMaxPayloadSize(SRTSOCKET u); public: // internal API // This is public so that it can be used directly in API implementation functions. diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index de6c8948b..ca75df277 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -1037,8 +1037,10 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } -bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW { + int resv = 0; + if (!this->sPacketFilterConfig.empty()) { // This means that the filter might have been installed before, @@ -1048,29 +1050,41 @@ bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errms if (!ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) { // Break silently. This should not happen - w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; - return false; + w_info = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return -1; } - const size_t efc_max_payload_size = CPacket::srtPayloadSize(ip_family) - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) << " bytes decreased by " << fc.extra_size - << " required for packet filter header"; - w_errmsg = log.str(); - return false; - } + resv += fc.extra_size; + w_info = "Packet Filter"; } - // Not checking AUTO to allow defaul 1456 bytes. - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (CPacket::srtPayloadSize(ip_family) - HAICRYPT_AUTHTAG_MAX))) + if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)) + { + resv += HAICRYPT_AUTHTAG_MAX; + if (!w_info.empty()) + w_info += " and "; + w_info += "AES_GCM mode"; + } + + return resv; +} + +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + int resv = extraPayloadReserve((w_errmsg)); + if (resv == -1) + return false; + + size_t valmax = CPacket::srtPayloadSize(ip_family) - resv; + + if (val > valmax) { std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << CPacket::srtPayloadSize(ip_family) - << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."; + log << "SRTO_PAYLOADSIZE: value " << val << "exceeds " << valmax + << " bytes"; + if (!w_errmsg.empty()) + log << " as limited by " << w_errmsg; + w_errmsg = log.str(); return false; } diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 20242512d..a04c782ef 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -348,6 +348,8 @@ struct CSrtConfig: CSrtMuxerConfig // This function returns the number of bytes that are allocated // for a single packet in the sender and receiver buffer. int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } + + int extraPayloadReserve(std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index c29e2c506..7e72238ba 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -312,9 +312,6 @@ SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) -// A macro for these above in case when the IP family is passed as a runtime value. -#define SRT_MAX_PLSIZE(famspec) ((famspec) == AF_INET ? SRT_MAX_PLSIZE_AF_INET : SRT_MAX_PLSIZE_AF_INET6) - // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; @@ -805,6 +802,9 @@ SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCK SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API int srt_getmaxpayloadsize(SRTSOCKET sock); + + typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; typedef struct SRT_MsgCtrl_ diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 031030cd7..8262b8ae0 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -169,6 +169,11 @@ int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } +int srt_getmaxpayloadsize(SRTSOCKET u) +{ + return CUDT::getMaxPayloadSize(u); +} + int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); } @@ -415,6 +420,9 @@ int srt_clock_type() return SRT_SYNC_CLOCK; } +// NOTE: crypto mode is defined regardless of the setting of +// ENABLE_AEAD_API_PREVIEW symbol. This can only block the symbol, +// but it doesn't change the symbol layout. const char* const srt_rejection_reason_msg [] = { "Unknown or erroneous", "Error in system calls", @@ -432,10 +440,9 @@ const char* const srt_rejection_reason_msg [] = { "Congestion controller type collision", "Packet Filter settings error", "Group settings collision", - "Connection timeout" -#ifdef ENABLE_AEAD_API_PREVIEW - ,"Crypto mode" -#endif + "Connection timeout", + "Crypto mode", + "Invalid configuration" }; // Deprecated, available in SRT API. @@ -456,14 +463,14 @@ extern const char* const srt_rejectreason_msg[] = { srt_rejection_reason_msg[13], srt_rejection_reason_msg[14], srt_rejection_reason_msg[15], - srt_rejection_reason_msg[16] -#ifdef ENABLE_AEAD_API_PREVIEW - , srt_rejection_reason_msg[17] -#endif + srt_rejection_reason_msg[16], + srt_rejection_reason_msg[17], + srt_rejection_reason_msg[18] }; const char* srt_rejectreason_str(int id) { + using namespace srt_logging; if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -471,7 +478,10 @@ const char* srt_rejectreason_str(int id) static const size_t ra_size = Size(srt_rejection_reason_msg); if (size_t(id) >= ra_size) + { + HLOGC(gglog.Error, log << "Invalid rejection code #" << id); return srt_rejection_reason_msg[0]; + } return srt_rejection_reason_msg[id]; } diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 05dd39330..b7344425e 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -393,7 +393,7 @@ void SrtCommon::InitParameters(string host, string path, map par) m_mode = par.at("mode"); } - int fam_to_limit_size = AF_INET6; // take the less one as default + size_t max_payload_size = 0; // Try to interpret host and adapter first sockaddr_any host_sa, adapter_sa; @@ -401,27 +401,31 @@ void SrtCommon::InitParameters(string host, string path, map par) if (host != "") { host_sa = CreateAddr(host); - fam_to_limit_size = host_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (host_sa.family() == AF_UNSPEC) Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } if (adapter != "") { adapter_sa = CreateAddr(adapter); - fam_to_limit_size = adapter_sa.family(); - if (fam_to_limit_size == AF_UNSPEC) + if (adapter_sa.family() == AF_UNSPEC) Error("Failed to interpret 'adapter' spec: " + adapter); if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) { Error("Both host and adapter specified and they use different IP versions"); } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; } - if (fam_to_limit_size != AF_INET) - fam_to_limit_size = AF_INET6; + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) @@ -479,12 +483,17 @@ void SrtCommon::InitParameters(string host, string path, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - size_t size_limit = (size_t)SRT_MAX_PLSIZE(fam_to_limit_size); - if (transmit_chunk_size > size_limit) - throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", size_limit, " bytes; this is not supported")); + if (transmit_chunk_size > max_payload_size) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assigning group configuration from a special "groupconfig" attribute. // This is the only way how you can set up this configuration at the listener side. @@ -598,6 +607,21 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + #if ENABLE_BONDING if (m_sock & SRTGROUP_MASK) { @@ -1435,6 +1459,19 @@ void SrtCommon::ConnectClient(string host, int port) transmit_error_storage.clear(); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + Verb() << " connected."; stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..d40d63dee 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -104,6 +104,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; vector m_group_nodes; string m_group_type; string m_group_config; From 49346a218b6f8e6739f8e1fdb4364142c242a476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 18 Oct 2023 13:47:00 +0200 Subject: [PATCH 105/517] Removed BytesPacketsCount. Shifted header size to a function parameter --- srtcore/core.cpp | 50 +++++++++++++++++------------------- srtcore/core.h | 6 ----- srtcore/group.cpp | 18 ++++++++----- srtcore/stats.h | 65 ++++++++--------------------------------------- 4 files changed, 46 insertions(+), 93 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 22876663f..765ffd2de 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -4715,7 +4715,6 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); - m_stats.setupHeaderSize(full_hdr_size); m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; @@ -5571,7 +5570,7 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } return iDropCnt; @@ -5595,7 +5594,7 @@ void srt::CUDT::setInitialRcvSeq(int32_t isn) const int iDropCnt = m_pRcvBuffer->dropAll(); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); sync::ScopedLock sl(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } m_pRcvBuffer->setStartSeqNo(isn); @@ -5738,7 +5737,6 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); - m_stats.setupHeaderSize(full_hdr_size); // exchange info for maximum flow window size m_iFlowWindowSize = w_hs.m_iFlightFlagSize; @@ -7452,17 +7450,17 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRcvFilterLoss = m_stats.rcvr.lossFilter.trace.count(); /* perf byte counters include all headers (SRT+UDP+IP) */ - perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr(); - perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr(); - perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr(); - perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr(); - perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr(); - perf->byteRcvLoss = m_stats.rcvr.lost.trace.bytesWithHdr(); + perf->byteSent = m_stats.sndr.sent.trace.bytesWithHdr(pktHdrSize); + perf->byteSentUnique = m_stats.sndr.sentUnique.trace.bytesWithHdr(pktHdrSize); + perf->byteRecv = m_stats.rcvr.recvd.trace.bytesWithHdr(pktHdrSize); + perf->byteRecvUnique = m_stats.rcvr.recvdUnique.trace.bytesWithHdr(pktHdrSize); + perf->byteRetrans = m_stats.sndr.sentRetrans.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvLoss = m_stats.rcvr.lost.trace.bytesWithHdr(pktHdrSize); perf->pktSndDrop = m_stats.sndr.dropped.trace.count(); perf->pktRcvDrop = m_stats.rcvr.dropped.trace.count(); - perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(); - perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(); + perf->byteSndDrop = m_stats.sndr.dropped.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvDrop = m_stats.rcvr.dropped.trace.bytesWithHdr(pktHdrSize); perf->pktRcvUndecrypt = m_stats.rcvr.undecrypted.trace.count(); perf->byteRcvUndecrypt = m_stats.rcvr.undecrypted.trace.bytes(); @@ -7479,22 +7477,22 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->pktRecvNAKTotal = m_stats.sndr.recvdNak.total.count(); perf->usSndDurationTotal = m_stats.m_sndDurationTotal; - perf->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr(); - perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr(); - perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr(); - perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr(); - perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr(); + perf->byteSentTotal = m_stats.sndr.sent.total.bytesWithHdr(pktHdrSize); + perf->byteSentUniqueTotal = m_stats.sndr.sentUnique.total.bytesWithHdr(pktHdrSize); + perf->byteRecvTotal = m_stats.rcvr.recvd.total.bytesWithHdr(pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.rcvr.recvdUnique.total.bytesWithHdr(pktHdrSize); + perf->byteRetransTotal = m_stats.sndr.sentRetrans.total.bytesWithHdr(pktHdrSize); perf->pktSndFilterExtraTotal = m_stats.sndr.sentFilterExtra.total.count(); perf->pktRcvFilterExtraTotal = m_stats.rcvr.recvdFilterExtra.total.count(); perf->pktRcvFilterSupplyTotal = m_stats.rcvr.suppliedByFilter.total.count(); perf->pktRcvFilterLossTotal = m_stats.rcvr.lossFilter.total.count(); - perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(); + perf->byteRcvLossTotal = m_stats.rcvr.lost.total.bytesWithHdr(pktHdrSize); perf->pktSndDropTotal = m_stats.sndr.dropped.total.count(); perf->pktRcvDropTotal = m_stats.rcvr.dropped.total.count(); // TODO: The payload is dropped. Probably header sizes should not be counted? - perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(); - perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr(); + perf->byteSndDropTotal = m_stats.sndr.dropped.total.bytesWithHdr(pktHdrSize); + perf->byteRcvDropTotal = m_stats.rcvr.dropped.total.bytesWithHdr(pktHdrSize); perf->pktRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.count(); perf->byteRcvUndecryptTotal = m_stats.rcvr.undecrypted.total.bytes(); @@ -9005,7 +9003,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); leaveCS(m_StatsLock); } } @@ -10122,8 +10120,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt * rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Decryption failed (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10139,8 +10137,8 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& const steady_clock::time_point tnow = steady_clock::now(); ScopedLock lg(m_StatsLock); - m_stats.rcvr.dropped.count(stats::BytesPacketsCount(iDropCnt* rpkt.getLength(), iDropCnt)); - m_stats.rcvr.undecrypted.count(stats::BytesPacketsCount(rpkt.getLength(), 1)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt* rpkt.getLength(), iDropCnt)); + m_stats.rcvr.undecrypted.count(stats::BytesPackets(rpkt.getLength(), 1)); if (frequentLogAllowed(tnow)) { LOGC(qrlog.Warn, log << CONID() << "Packet not encrypted (seqno %" << u->m_Packet.getSeqNo() << "), dropped " @@ -10340,7 +10338,7 @@ int srt::CUDT::processData(CUnit* in_unit) ScopedLock lg(m_StatsLock); const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.lost.count(stats::BytesPacketsCount(loss * avgpayloadsz, (uint32_t) loss)); + m_stats.rcvr.lost.count(stats::BytesPackets(loss * avgpayloadsz, (uint32_t) loss)); HLOGC(qrlog.Debug, log << CONID() << "LOSS STATS: n=" << loss << " SEQ: [" << CSeqNo::incseq(m_iRcvCurrPhySeqNo) << " " diff --git a/srtcore/core.h b/srtcore/core.h index 4d3a84f42..41042ff50 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1165,12 +1165,6 @@ class CUDT int64_t sndDuration; // real time for sending time_point sndDurationCounter; // timers to record the sending Duration - void setupHeaderSize(int hsize) - { - sndr.setupHeaderSize(hsize); - rcvr.setupHeaderSize(hsize); - } - } m_stats; public: diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 02ad7b759..934550847 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2379,6 +2379,12 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) const steady_clock::time_point currtime = steady_clock::now(); + // NOTE: Potentially in the group we might be using both IPv4 and IPv6 + // links and sending a single packet over these two links could be different. + // These stats then don't make much sense in this form, this has to be + // redesigned. We use the header size as per IPv4, as it was everywhere. + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(AF_INET); + memset(perf, 0, sizeof *perf); ScopedLock gg(m_GroupLock); @@ -2389,17 +2395,17 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) perf->pktRecvUnique = m_stats.recv.trace.count(); perf->pktRcvDrop = m_stats.recvDrop.trace.count(); - perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(); - perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(); - perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(); + perf->byteSentUnique = m_stats.sent.trace.bytesWithHdr(pktHdrSize); + perf->byteRecvUnique = m_stats.recv.trace.bytesWithHdr(pktHdrSize); + perf->byteRcvDrop = m_stats.recvDrop.trace.bytesWithHdr(pktHdrSize); perf->pktSentUniqueTotal = m_stats.sent.total.count(); perf->pktRecvUniqueTotal = m_stats.recv.total.count(); perf->pktRcvDropTotal = m_stats.recvDrop.total.count(); - perf->byteSentUniqueTotal = m_stats.sent.total.bytesWithHdr(); - perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(); - perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(); + perf->byteSentUniqueTotal = m_stats.sent.total.bytesWithHdr(pktHdrSize); + perf->byteRecvUniqueTotal = m_stats.recv.total.bytesWithHdr(pktHdrSize); + perf->byteRcvDropTotal = m_stats.recvDrop.total.bytesWithHdr(pktHdrSize); const double interval = static_cast(count_microseconds(currtime - m_stats.tsLastSampleTime)); perf->mbpsSendRate = double(perf->byteSent) * 8.0 / interval; diff --git a/srtcore/stats.h b/srtcore/stats.h index 55d8d00d9..947489eb1 100644 --- a/srtcore/stats.h +++ b/srtcore/stats.h @@ -22,8 +22,6 @@ namespace stats class Packets { public: - typedef Packets count_type; - Packets() : m_count(0) {} Packets(uint32_t num) : m_count(num) {} @@ -48,15 +46,15 @@ class Packets uint32_t m_count; }; -class BytesPacketsCount +class BytesPackets { public: - BytesPacketsCount() + BytesPackets() : m_bytes(0) , m_packets(0) {} - BytesPacketsCount(uint64_t bytes, uint32_t n = 1) + BytesPackets(uint64_t bytes, uint32_t n = 1) : m_bytes(bytes) , m_packets(n) {} @@ -84,43 +82,23 @@ class BytesPacketsCount return m_packets; } - BytesPacketsCount& operator+= (const BytesPacketsCount& other) + BytesPackets& operator+= (const BytesPackets& other) { m_bytes += other.m_bytes; m_packets += other.m_packets; return *this; } + uint64_t bytesWithHdr(size_t hdr_size) const + { + return m_bytes + m_packets * hdr_size; + } + protected: uint64_t m_bytes; uint32_t m_packets; }; -class BytesPackets: public BytesPacketsCount -{ -public: - typedef BytesPacketsCount count_type; - - // Set IPv4-based header size value as a fallback. This will be fixed upon connection. - BytesPackets() - : m_zPacketHeaderSize(CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE) - {} - -public: - - void setupHeaderSize(int size) - { - m_zPacketHeaderSize = uint64_t(size); - } - - uint64_t bytesWithHdr() const - { - return m_bytes + m_packets * m_zPacketHeaderSize; - } - -private: - uint64_t m_zPacketHeaderSize; -}; template struct Metric @@ -128,7 +106,7 @@ struct Metric METRIC_TYPE trace; METRIC_TYPE total; - void count(typename METRIC_TYPE::count_type val) + void count(BASE_METRIC_TYPE val) { trace += val; total += val; @@ -166,16 +144,6 @@ struct Sender Metric recvdAck; // The number of ACK packets received by the sender. Metric recvdNak; // The number of ACK packets received by the sender. - void setupHeaderSize(int hdr_size) - { -#define SETHSIZE(var) var.setupHeaderSize(hdr_size) - SETHSIZE(sent); - SETHSIZE(sentUnique); - SETHSIZE(sentRetrans); - SETHSIZE(dropped); -#undef SETHSIZE - } - void reset() { sent.reset(); @@ -219,19 +187,6 @@ struct Receiver Metric sentAck; // The number of ACK packets sent by the receiver. Metric sentNak; // The number of NACK packets sent by the receiver. - void setupHeaderSize(int hdr_size) - { -#define SETHSIZE(var) var.setupHeaderSize(hdr_size) - SETHSIZE(recvd); - SETHSIZE(recvdUnique); - SETHSIZE(recvdRetrans); - SETHSIZE(lost); - SETHSIZE(dropped); - SETHSIZE(recvdBelated); - SETHSIZE(undecrypted); -#undef SETHSIZE - } - void reset() { recvd.reset(); From e1262cc99d4733972abdd8b76d5fc61c43c72540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 19 Oct 2023 13:32:24 +0200 Subject: [PATCH 106/517] Added documentation for srt_getmaxpayloadsize --- docs/API/API-functions.md | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index f4a7aa07b..8ae4e96d4 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -19,6 +19,7 @@ | [srt_bind_acquire](#srt_bind_acquire) | Acquires a given UDP socket instead of creating one | | [srt_getsockstate](#srt_getsockstate) | Gets the current status of the socket | | [srt_getsndbuffer](#srt_getsndbuffer) | Retrieves information about the sender buffer | +| [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) | Retrieves the information about the maximum payload size in a single packet | | [srt_close](#srt_close) | Closes the socket or group and frees all used resources | | | | @@ -293,6 +294,7 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne * [srt_bind_acquire](#srt_bind_acquire) * [srt_getsockstate](#srt_getsockstate) * [srt_getsndbuffer](#srt_getsndbuffer) +* [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) * [srt_close](#srt_close) @@ -540,6 +542,58 @@ This function can be used for diagnostics. It is especially useful when the socket needs to be closed asynchronously. +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### srt_getmaxpayloadsize + +``` +int srt_getmaxpayloadsize(SRTSOCKET u); +``` + +Returns the maximum number of bytes that fit in a single packet. Useful only in +live mode (when `SRTO_TSBPDMODE` is true). The socket must be bound (see +[srt_bind](#srt_bind)) or connected (see [srt_connect](#srt_connect)) +to use this function. Note that in case when the socket is bound to an IPv6 +wildcard address and it is dual-stack (`SRTO_IPV6ONLY` is set to false), this +function returns the correct value only if the socket is connected, otherwise +it will return the value always as if the connection was made from an IPv6 peer +(including when you call it on a listening socket). + +This function is only useful for the application to check if it is able to use +a payload of certain size in the live mode, or after connection, if the application +can send payloads of certain size. This is useful only in assertions, as if the +[`SRTO_PAYLOADSIZE`](API_socket-options.md#SRTO_PAYLOADSIZE) option is to be +set to a non-default value (for which the one returned by this function is the +maximum value), this option should be modified before connection and on both +parties, regarding the settings applied on the socket. + +The returned value is the maximum number of bytes that can be put in a single +packet regarding: + +* The current MTU size (`SRTO_MSS`) +* The IP version (IPv4 or IPv6) +* The `SRTO_CRYPTOMODE` setting (bytes reserved for AEAD authentication tag) +* The `SRTO_PACKETFILTER` setting (bytes reserved for extra field in a FEC control packet) + +With default options this value should be 1456 for IPv4 and 1444 for IPv6. + + +| Returns | | +|:----------------------------- |:------------------------------------------------- | +| The maximum payload size (>0) | If succeeded | +| `SRT_ERROR` | Usage error | +| | | + +| Errors | | +|:--------------------------------------- |:----------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | Socket [`u`](#u) is not bound | +| | | + + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- From f91f75a9619a1cd7496818527fd2fa8231c18ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 16:00:16 +0100 Subject: [PATCH 107/517] Added group contents check and configurable sleep for group/listener tests --- test/test_epoll.cpp | 91 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 38624a62e..2c98ec38d 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -548,6 +548,9 @@ TEST(CEPoll, ThreadedUpdate) void testListenerReady(const bool LATE_CALL, size_t nmembers) { + bool is_single = true; + bool want_sleep = !TestEnv::me->OptionPresent("nosleep"); + sockaddr_in sa; memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; @@ -564,6 +567,7 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) caller_sock = srt_create_group(SRT_GTYPE_BROADCAST); int on = 1; EXPECT_NE(srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &on, sizeof on), SRT_ERROR); + is_single = false; } else { @@ -588,7 +592,8 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // We don't need the caller to be async, it can hang up here. for (size_t i = 0; i < nmembers; ++i) { - connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); })); } @@ -596,8 +601,11 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) std::cout << "STARTED connecting...\n"; } - std::cout << "Sleeping 1s...\n"; - this_thread::sleep_for(chrono::milliseconds(1000)); + if (want_sleep) + { + std::cout << "Sleeping 1s...\n"; + this_thread::sleep_for(chrono::milliseconds(1000)); + } // What is important is that the accepted socket is now reporting in // on the listener socket. So let's create an epoll. @@ -616,7 +624,8 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // We don't need the caller to be async, it can hang up here. for (size_t i = 0; i < nmembers; ++i) { - connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa]() { + connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa, i]() { + std::cout << "[T:" << i << "] CALLING\n"; return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); })); } @@ -640,8 +649,9 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) std::cout << "With >1 members, check if there's still UPDATE pending\n"; // Spawn yet another connection within the group, just to get the update auto extra_call = std::async(std::launch::async, [&caller_sock, &sa]() { - return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); - }); + std::cout << "[T:X] CALLING (expected failure)\n"; + return srt_connect(caller_sock, (sockaddr*)& sa, sizeof(sa)); + }); // For 2+ members, additionally check if there AREN'T any // further acceptance members, but there are UPDATEs. EXPECT_EQ(srt_epoll_uwait(eid_postcheck, fdset, 1, 5000), 1); @@ -649,15 +659,80 @@ void testListenerReady(const bool LATE_CALL, size_t nmembers) // SUBSCRIBED EVENTS: IN, UPDATE. // expected: UPDATE only. EXPECT_EQ(fdset[0].events, SRT_EPOLL_UPDATE); - EXPECT_NE(extra_call.get(), SRT_INVALID_SOCK); + SRTSOCKET joined = extra_call.get(); + EXPECT_NE(joined, SRT_INVALID_SOCK); + std::cout << Sprint("Extra joined: @", joined, "\n"); + } + + std::vector gdata; + + if (!is_single) + { + EXPECT_EQ(sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Listener) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); } std::cout << "Joining connector thread(s)\n"; for (size_t i = 0; i < nmembers; ++i) { - EXPECT_NE(connect_res[i].get(), SRT_INVALID_SOCK); + std::cout << "Join: #" << i << ":\n"; + SRTSOCKET called_socket = connect_res[i].get(); + std::cout << "... " << called_socket << std::endl; + EXPECT_NE(called_socket, SRT_INVALID_SOCK); } + if (!is_single) + { + EXPECT_EQ(caller_sock & SRTGROUP_MASK, SRTGROUP_MASK); + // +1 because we have added one more caller to check UPDATE event. + size_t inoutlen = nmembers+1; + gdata.resize(inoutlen); + int groupndata = srt_group_data(caller_sock, gdata.data(), (&inoutlen)); + EXPECT_NE(groupndata, SRT_ERROR); + + std::ostringstream sout; + if (groupndata == SRT_ERROR) + sout << "ERROR: " << srt_getlasterror_str() << " OUTLEN: " << inoutlen << std::endl; + else + { + // Just to display the members + sout << "(Caller) Members: "; + + for (int i = 0; i < groupndata; ++i) + sout << "@" << gdata[i].id << " "; + sout << std::endl; + } + + std::cout << sout.str(); + + if (want_sleep) + { + std::cout << "Sleep for 3 seconds to avoid closing-in-between\n"; + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + } + + std::cout << "Releasing EID resources and all sockets\n"; + srt_epoll_release(eid); srt_epoll_release(eid_postcheck); From 8072420ad762feb1c67cd1f8c3b7da1775ae18a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 16:50:42 +0100 Subject: [PATCH 108/517] Fixed warn-error build break on mac --- srtcore/socketconfig.cpp | 2 +- testing/srt-test-live.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index ca75df277..446d9b201 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -1058,7 +1058,7 @@ int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW w_info = "Packet Filter"; } - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)) + if (this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) { resv += HAICRYPT_AUTHTAG_MAX; if (!w_info.empty()) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..aa6b45d6a 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -300,7 +300,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From 188cbd1b4b2a43c50792b4a37e1a23dd447d6c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 30 Oct 2023 17:15:56 +0100 Subject: [PATCH 109/517] Fixed build break on more pedantic compilers --- test/test_epoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 2c98ec38d..c720a1075 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -823,7 +823,7 @@ void testMultipleListenerReady(const bool LATE_CALL) connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { this_thread::sleep_for(chrono::milliseconds(1)); - return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); })); @@ -856,7 +856,7 @@ void testMultipleListenerReady(const bool LATE_CALL) connect_res.push_back(std::async(std::launch::async, [&caller_sock, &sa2]() { this_thread::sleep_for(chrono::milliseconds(1)); - return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa)); + return srt_connect(caller_sock, (sockaddr*)& sa2, sizeof(sa2)); })); std::cout << "STARTED connecting...\n"; From e8cd37d25d45881da518ae794e01be55e7612378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 23 Jan 2024 20:05:12 +0100 Subject: [PATCH 110/517] Further fixes for OptionProxy. Some cosmetics in the docs --- apps/apputil.hpp | 31 +++++++++++++++++++++++++------ docs/API/API-functions.md | 6 ++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/apps/apputil.hpp b/apps/apputil.hpp index c79cf984e..1a0b158e0 100644 --- a/apps/apputil.hpp +++ b/apps/apputil.hpp @@ -350,22 +350,41 @@ struct OptionSetterProxy struct OptionProxy { - const OptionSetterProxy& parent; + OptionSetterProxy& parent; SRT_SOCKOPT opt; - template - OptionProxy& operator=(Type&& val) +#define SPEC(type) \ + OptionProxy& operator=(const type& val)\ + {\ + parent.result = srt_setsockflag(parent.s, opt, &val, sizeof val);\ + return *this;\ + } + + SPEC(int32_t); + SPEC(int64_t); + SPEC(bool); +#undef SPEC + + template + OptionProxy& operator=(const char (&val)[N]) + { + parent.result = srt_setsockflag(parent.s, opt, val, N-1); + return *this; + } + + OptionProxy& operator=(const std::string& val) { - Type vc(val); - srt_setsockflag(parent.s, opt, &vc, sizeof vc); + parent.result = srt_setsockflag(parent.s, opt, val.c_str(), val.size()); return *this; } }; - OptionProxy operator[](SRT_SOCKOPT opt) const + OptionProxy operator[](SRT_SOCKOPT opt) { return OptionProxy {*this, opt}; } + + operator int() { return result; } }; inline OptionSetterProxy setopt(SRTSOCKET socket) diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 7608d32f0..368216005 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -639,7 +639,7 @@ below). * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of the returned object -General requirements for parameter correctness: +General requirements for a parameter correctness: * `lsn` must be first [bound](#srt_bind) and [listening](#srt_listen) @@ -656,7 +656,7 @@ depends on the address type used in the `srt_bind` call for `lsn`. If unsure in a particular situation, it is recommended that you use `sockaddr_storage` or `srt::sockaddr_any`. -If the `lsn` listener socket is blocking mode (if +If the `lsn` listener socket is in the blocking mode (if [`SRTO_RCVSYN`](API-socket-options.md#SRTO_RCVSYN) is set to true, which is default), the call will block until the incoming connection is ready for extraction. Otherwise, the call always returns immediately, possibly with @@ -707,6 +707,8 @@ request. This feature is prone to more tricky rules, however: continue blocking. If you want to use only one thread for accepting connections from potentially multiple listening sockets in the blocking mode, you should use [`srt_accept_bond`](#srt_accept_bond) instead. + Note though that this function is actually a wrapper that changes locally + to the nonblocking mode on all these listeners and uses epoll internally. * If at the moment multiple listener sockets have received connection request and you query them all for readiness epoll flags (by calling From 8ad69ed81243466e633bc02960ef0f87cb786da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 15 Feb 2024 13:38:20 +0100 Subject: [PATCH 111/517] Fixed a suggested uninitialized variable --- srtcore/buffer_rcv.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 488b73f35..235909e90 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -364,6 +364,11 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iDropPos); avail_range = 1; } + else + { + avail_packet = NULL; + avail_range = 0; + } IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); From bbced79792597a1d11f9051be9ac157658878fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 09:19:47 +0100 Subject: [PATCH 112/517] Renamed eclipsed variable --- srtcore/buffer_rcv.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 235909e90..a7693a839 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -1495,7 +1495,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - int pos = incPos(m_iStartPos, offset); + int frompos = incPos(m_iStartPos, offset); // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1506,7 +1506,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) int ret_off = m_iMaxPosOff; int end_off = offPos(m_iStartPos, m_iEndPos); - if (pos < end_off) + if (frompos < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. @@ -1560,7 +1560,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << pos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the From 5136ca2c3eb6ffb8c6c92fe55cc154cb2513151d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 10:32:39 +0100 Subject: [PATCH 113/517] Added doxy description for some functions. Applied NonOrder in names for out-of-order concerns --- srtcore/buffer_rcv.cpp | 100 ++++++++++++++++++----------------------- srtcore/buffer_rcv.h | 59 +++++++++++++++++------- srtcore/utilities.h | 2 +- 3 files changed, 89 insertions(+), 72 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a7693a839..831840967 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -126,8 +126,8 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) - , m_numRandomPackets(0) - , m_iFirstRandomMsgPos(-1) + , m_numNonOrderPackets(0) + , m_iFirstNonOrderMsgPos(-1) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -337,8 +337,8 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // With TSBPD enabled packets are always assumed in order (the flag is ignored). if (!m_tsbpd.isEnabled() && m_bMessageAPI && !unit->m_Packet.getMsgOrderFlag()) { - ++m_numRandomPackets; - onInsertNotInOrderPacket(newpktpos); + ++m_numNonOrderPackets; + onInsertNonOrderPacket(newpktpos); } updateNonreadPos(); @@ -350,13 +350,13 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) avail_packet = &packetAt(m_iStartPos); avail_range = offPos(m_iStartPos, m_iEndPos); } - else if (!m_tsbpd.isEnabled() && m_iFirstRandomMsgPos != -1) + else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) { // In case when TSBPD is off, we take into account the message mode // where messages may potentially span for multiple packets, therefore // the only "next deliverable" is the first complete message that satisfies // the order requirement. - avail_packet = &packetAt(m_iFirstRandomMsgPos); + avail_packet = &packetAt(m_iFirstNonOrderMsgPos); avail_range = 1; } else if (m_iDropPos != m_iEndPos) @@ -379,18 +379,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) } -// This function should be called after having m_iEndPos -// has somehow be set to position of a non-empty cell. -// This can happen by two reasons: -// - the cell has been filled by incoming packet -// - the value has been reset due to shifted m_iStartPos -// This means that you have to search for a new gap and -// update the m_iEndPos and m_iDropPos fields, or set them -// both to the end of range. -// -// prev_max_pos should be the position represented by m_iMaxPosOff. -// Passed because it is already calculated in insert(), otherwise -// it would have to be calculated here again. void CRcvBuffer::updateGapInfo(int prev_max_pos) { int pos = m_iEndPos; @@ -474,7 +462,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) updateNonreadPos(); } if (!m_tsbpd.isEnabled() && m_bMessageAPI) - updateFirstReadableRandom(); + updateFirstReadableNonOrder(); IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); return iDropCnt; @@ -613,9 +601,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro } if (!m_tsbpd.isEnabled() && m_bMessageAPI) { - if (!checkFirstReadableRandom()) - m_iFirstRandomMsgPos = -1; - updateFirstReadableRandom(); + if (!checkFirstReadableNonOrder()) + m_iFirstNonOrderMsgPos = -1; + updateFirstReadableNonOrder(); } IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); @@ -645,14 +633,14 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstRandomMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } //const bool canReadInOrder = m_iFirstNonreadPos != m_iStartPos; - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstRandomMsgPos; + const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -696,8 +684,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair 0 && m_iFirstRandomMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } int CRcvBuffer::getRcvDataSize() const @@ -1028,8 +1016,8 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const if (haveInorderPackets) return true; - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - return (m_numRandomPackets > 0 && m_iFirstRandomMsgPos != -1); + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); } if (!haveInorderPackets) @@ -1053,11 +1041,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no const PacketInfo info = {packet.getSeqNo(), false, time_point()}; return info; } - SRT_ASSERT((!m_bMessageAPI && m_numRandomPackets == 0) || m_bMessageAPI); - if (m_iFirstRandomMsgPos >= 0) + SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); + if (m_iFirstNonOrderMsgPos >= 0) { - SRT_ASSERT(m_numRandomPackets > 0); - const CPacket& packet = packetAt(m_iFirstRandomMsgPos); + SRT_ASSERT(m_numNonOrderPackets > 0); + const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); const PacketInfo info = {packet.getSeqNo(), true, time_point()}; return info; } @@ -1107,9 +1095,9 @@ bool CRcvBuffer::dropUnitInPos(int pos) } else if (m_bMessageAPI && !packetAt(pos).getMsgOrderFlag()) { - --m_numRandomPackets; - if (pos == m_iFirstRandomMsgPos) - m_iFirstRandomMsgPos = -1; + --m_numNonOrderPackets; + if (pos == m_iFirstNonOrderMsgPos) + m_iFirstNonOrderMsgPos = -1; } releaseUnitInPos(pos); return true; @@ -1185,9 +1173,9 @@ int CRcvBuffer::findLastMessagePkt() return -1; } -void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(int insertPos) { - if (m_numRandomPackets == 0) + if (m_numNonOrderPackets == 0) return; // If the following condition is true, there is already a packet, @@ -1196,7 +1184,7 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstRandomMsgPos >= 0) + if (m_iFirstNonOrderMsgPos >= 0) return; // Just a sanity check. This function is called when a new packet is added. @@ -1209,34 +1197,34 @@ void CRcvBuffer::onInsertNotInOrderPacket(int insertPos) //if ((boundary & PB_FIRST) && (boundary & PB_LAST)) //{ // // This packet can be read out of order - // m_iFirstRandomMsgPos = insertPos; + // m_iFirstNonOrderMsgPos = insertPos; // return; //} const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNotInOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); if (!hasLast) return; const int firstPktPos = (boundary & PB_FIRST) ? insertPos - : scanNotInOrderMessageLeft(insertPos, msgNo); + : scanNonOrderMessageLeft(insertPos, msgNo); if (firstPktPos < 0) return; - m_iFirstRandomMsgPos = firstPktPos; + m_iFirstNonOrderMsgPos = firstPktPos; return; } -bool CRcvBuffer::checkFirstReadableRandom() +bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numRandomPackets <= 0 || m_iFirstRandomMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) return false; const int endPos = incPos(m_iStartPos, m_iMaxPosOff); int msgno = -1; - for (int pos = m_iFirstRandomMsgPos; pos != endPos; pos = incPos(pos)) + for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1257,16 +1245,16 @@ bool CRcvBuffer::checkFirstReadableRandom() return false; } -void CRcvBuffer::updateFirstReadableRandom() +void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numRandomPackets <= 0 || m_iFirstRandomMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) return; if (m_iMaxPosOff == 0) return; // TODO: unused variable outOfOrderPktsRemain? - int outOfOrderPktsRemain = (int) m_numRandomPackets; + int outOfOrderPktsRemain = (int) m_numNonOrderPackets; // Search further packets to the right. // First check if there are packets to the right. @@ -1309,7 +1297,7 @@ void CRcvBuffer::updateFirstReadableRandom() if (boundary & PB_LAST) { - m_iFirstRandomMsgPos = posFirst; + m_iFirstNonOrderMsgPos = posFirst; return; } @@ -1320,7 +1308,7 @@ void CRcvBuffer::updateFirstReadableRandom() return; } -int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. @@ -1351,7 +1339,7 @@ int CRcvBuffer::scanNotInOrderMessageRight(const int startPos, int msgNo) const return -1; } -int CRcvBuffer::scanNotInOrderMessageLeft(const int startPos, int msgNo) const +int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 22393dbcd..70099d355 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -244,16 +244,45 @@ class CRcvBuffer }; - /// Insert a unit into the buffer. - /// Similar to CRcvBuffer::addData(CUnit* unit, int offset) + /// Inserts the unit with the data packet into the receiver buffer. + /// The result inform about the situation with the packet attempted + /// to be inserted and the readability of the buffer. /// - /// @param [in] unit pointer to a data unit containing new packet - /// @param [in] offset offset from last ACK point. + /// @param [PASS] unit The unit that should be placed in the buffer + /// + /// @return The InsertInfo structure where: + /// * result: the result of insertion, which is: + /// * INSERTED: successfully placed in the buffer + /// * REDUNDANT: not placed, the packet is already there + /// * BELATED: not placed, its sequence is in the past + /// * DISCREPANCY: not placed, the sequence is far future or OOTB + /// * first_seq: the earliest sequence number now avail for reading + /// * avail_range: how many packets are available for reading (1 if unknown) + /// * first_time: the play time of the earliest read-available packet + /// If there is no available packet for reading, first_seq == SRT_SEQNO_NONE. /// - /// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo. - /// -3 if a packet is offset is ahead the buffer capacity. - // TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value. InsertInfo insert(CUnit* unit); + + /// Update the values of `m_iEndPos` and `m_iDropPos` in + /// case when `m_iEndPos` was updated to a position of a + /// nonempty cell. + /// + /// This function should be called after having m_iEndPos + /// has somehow be set to position of a non-empty cell. + /// This can happen by two reasons: + /// + /// - the cell has been filled by incoming packet + /// - the value has been reset due to shifted m_iStartPos + /// + /// This means that you have to search for a new gap and + /// update the m_iEndPos and m_iDropPos fields, or set them + /// both to the end of range if there are no loss gaps. + /// + /// The @a prev_max_pos parameter is passed here because it is already + /// calculated in insert(), otherwise it would have to be calculated here again. + /// + /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` + /// void updateGapInfo(int prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). @@ -495,12 +524,12 @@ class CRcvBuffer int findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNotInOrderPacket(int insertpos); - // Check if m_iFirstRandomMsgPos is still readable. - bool checkFirstReadableRandom(); - void updateFirstReadableRandom(); - int scanNotInOrderMessageRight(int startPos, int msgNo) const; - int scanNotInOrderMessageLeft(int startPos, int msgNo) const; + void onInsertNonOrderPacket(int insertpos); + // Check if m_iFirstNonOrderMsgPos is still readable. + bool checkFirstReadableNonOrder(); + void updateFirstReadableNonOrder(); + int scanNonOrderMessageRight(int startPos, int msgNo) const; + int scanNonOrderMessageLeft(int startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -566,12 +595,12 @@ class CRcvBuffer int m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) - size_t m_numRandomPackets; // The number of stored packets with "inorder" flag set to false + size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstRandomMsgPos; + int m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 258a2fdc8..3d9cf1c09 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () { return 0!= get(); } + operator bool () const { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From f6e0271c72a16dd12942651eae4cec28a39f586c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 11:28:24 +0100 Subject: [PATCH 114/517] Removed wrong fix. Fixed C++11 style initialization of PacketInfo --- srtcore/buffer_rcv.cpp | 26 ++++++++++++++++++++------ srtcore/utilities.h | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 831840967..566854984 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -986,20 +986,34 @@ int CRcvBuffer::getRcvDataSize(int& bytes, int& timespan) const CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const { - // Check the state of the very first packet first + // Default: no packet available. + PacketInfo pi = { SRT_SEQNO_NONE, false, time_point() }; + + const CPacket* pkt = NULL; + + // Very first packet available with no gap. if (m_entries[m_iStartPos].status == EntryState_Avail) { SRT_ASSERT(m_entries[m_iStartPos].pUnit); - return { m_iStartSeqNo, false /*no gap*/, getPktTsbPdTime(packetAt(m_iStartPos).getMsgTimeStamp()) }; + pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - if (m_iDropPos != m_iEndPos) + else if (m_iDropPos != m_iEndPos) + { + SRT_ASSERT(m_entries[m_iDropPos].pUnit); + pkt = &packetAt(m_iDropPos); + pi.seq_gap = true; // Available, but after a drop. + } + else { - const CPacket& pkt = packetAt(m_iDropPos); - return { pkt.getSeqNo(), true, getPktTsbPdTime(pkt.getMsgTimeStamp()) }; + // If none of them point to a valid packet, + // there is no packet available; + return pi; } - return { SRT_SEQNO_NONE, false, time_point() }; + pi.seqno = pkt->getSeqNo(); + pi.tsbpd_time = getPktTsbPdTime(pkt->getMsgTimeStamp()); + return pi; } std::pair CRcvBuffer::getAvailablePacketsRange() const diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 3d9cf1c09..258a2fdc8 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -682,7 +682,7 @@ class UniquePtr: public std::auto_ptr bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } - operator bool () const { return 0!= get(); } + operator bool () { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable From 072a8c482ec9a8963a9ca0efa0a4ac2ff0df9688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Fri, 16 Feb 2024 16:29:55 +0100 Subject: [PATCH 115/517] Refax: CRcvBuffer extracted some parts of insert() to separate functions --- srtcore/buffer_rcv.cpp | 305 ++++++++++++++++++++++------------------- srtcore/buffer_rcv.h | 6 + 2 files changed, 167 insertions(+), 144 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 566854984..97b76d755 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -144,7 +144,7 @@ CRcvBuffer::~CRcvBuffer() { if (!it->pUnit) continue; - + m_pUnitQueue->makeUnitFree(it->pUnit); it->pUnit = NULL; } @@ -167,9 +167,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - int32_t avail_seq; - int avail_range; - if (offset < 0) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); @@ -181,31 +178,12 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); - // Calculation done for the sake of possible discrepancy - // in order to inform the caller what to do. - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_seq = packetAt(m_iStartPos).getSeqNo(); - avail_range = m_iEndPos - m_iStartPos; - } - else if (m_iDropPos == m_iEndPos) - { - avail_seq = SRT_SEQNO_NONE; - avail_range = 0; - } - else - { - avail_seq = packetAt(m_iDropPos).getSeqNo(); - - // We don't know how many packets follow it exactly, - // but in this case it doesn't matter. We know that - // at least one is there. - avail_range = 1; - } + InsertInfo ireport (InsertInfo::DISCREPANCY); + getAvailInfo((ireport)); IF_HEAVY_LOGGING(debugShowState((debug_source + " overflow").c_str())); - return InsertInfo(InsertInfo::DISCREPANCY, avail_seq, avail_range); + return ireport; } // TODO: Don't do assert here. Process this situation somehow. @@ -243,95 +221,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time; + time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); - int prev_max_pos = incPos(m_iStartPos, prev_max_off); - - // Update flags - // Case [A] - if (extended_end) - { - // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) - { - // THIS means that the new packet didn't CAUSE a gap - if (m_iMaxPosOff == prev_max_off + 1) - { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, - // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iDropPos = m_iEndPos; - } - else - { - // Otherwise we have a drop-after-gap candidate - // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - } - } - } - // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. - - // Here you can use prev_max_pos as the position represented - // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) - { - // Case [D]: inserted a packet at the first gap following the - // contiguous region. This makes a potential to extend the - // contiguous region and we need to find its end. - - // If insertion happened at the very first packet, it is the - // new earliest packet now. In any other situation under this - // condition there's some contiguous packet range preceding - // this position. - if (m_iEndPos == m_iStartPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - - updateGapInfo(prev_max_pos); - } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) - { - // Case [C]: the newly inserted packet precedes the - // previous earliest delivery position after drop, - // that is, there is now a "better" after-drop delivery - // candidate. - - // New position updated a valid packet on an earlier - // position than the drop position was before, although still - // following a gap. - // - // We know it because if the position has filled a gap following - // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a - // contiguous series of valid packets following a gap, hence - // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; - - // If there's an inserted packet BEFORE drop-pos (which makes it - // a new drop-pos), while the very first packet is absent (the - // below condition), it means we have a new earliest-available - // packet. Otherwise we would have only a newly updated drop - // position, but still following some earlier contiguous range - // of valid packets - so it's earlier than previous drop, but - // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) - { - earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); - } - } - // OTHERWISE: case [D] in which nothing is to be updated. + InsertInfo ireport (InsertInfo::INSERTED); + ireport.first_time = earlier_time; // If packet "in order" flag is zero, it can be read out of order. // With TSBPD enabled packets are always assumed in order (the flag is ignored). @@ -343,40 +236,164 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) updateNonreadPos(); - CPacket* avail_packet = NULL; - - if (m_entries[m_iStartPos].pUnit && m_entries[m_iStartPos].status == EntryState_Avail) - { - avail_packet = &packetAt(m_iStartPos); - avail_range = offPos(m_iStartPos, m_iEndPos); - } - else if (!m_tsbpd.isEnabled() && m_iFirstNonOrderMsgPos != -1) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - avail_packet = &packetAt(m_iFirstNonOrderMsgPos); - avail_range = 1; - } - else if (m_iDropPos != m_iEndPos) - { - avail_packet = &packetAt(m_iDropPos); - avail_range = 1; - } - else - { - avail_packet = NULL; - avail_range = 0; - } + // This updates only the first_seq and avail_range fields. + getAvailInfo((ireport)); IF_RCVBUF_DEBUG(scoped_log.ss << " returns 0 (OK)"); IF_HEAVY_LOGGING(debugShowState((debug_source + " ok").c_str())); - if (avail_packet) - return InsertInfo(InsertInfo::INSERTED, avail_packet->getSeqNo(), avail_range, earlier_time); - else - return InsertInfo(InsertInfo::INSERTED); // No packet candidate (NOTE: impossible in live mode) + return ireport; +} + +void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) +{ + int fallback_pos = -1; + if (!m_tsbpd.isEnabled()) + { + // In case when TSBPD is off, we take into account the message mode + // where messages may potentially span for multiple packets, therefore + // the only "next deliverable" is the first complete message that satisfies + // the order requirement. + // NOTE THAT this field can as well be -1 already. + fallback_pos = m_iFirstNonOrderMsgPos; + } + else if (m_iDropPos != m_iEndPos) + { + // With TSBPD regard the drop position (regardless if + // TLPKTDROP is currently on or off), if "exists", that + // is, m_iDropPos != m_iEndPos. + fallback_pos = m_iDropPos; + } + + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position (unless it's -1). + const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); + if (pkt) + { + w_if.first_seq = pkt->getSeqNo(); + } +} + + +const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +{ + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + pos = m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + } + + if (pos == -1) + { + w_span = 0; + return NULL; + } + + SRT_ASSERT(m_entries[pos].pUnit != NULL); + + // TODO: we know that at least 1 packet is available, but only + // with m_iEndPos we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_span = 1; + return &packetAt(pos); +} + +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +{ + time_point earlier_time; + + int prev_max_pos = incPos(m_iStartPos, prev_max_off); + + // Update flags + // Case [A] + if (extended_end) + { + // THIS means that the buffer WAS CONTIGUOUS BEFORE. + if (m_iEndPos == prev_max_pos) + { + // THIS means that the new packet didn't CAUSE a gap + if (m_iMaxPosOff == prev_max_off + 1) + { + // This means that m_iEndPos now shifts by 1, + // and m_iDropPos must be shifted together with it, + // as there's no drop to point. + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iDropPos = m_iEndPos; + } + else + { + // Otherwise we have a drop-after-gap candidate + // which is the currently inserted packet. + // Therefore m_iEndPos STAYS WHERE IT IS. + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + } + } + } + // + // Since this place, every newpktpos is in the range + // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + + // Here you can use prev_max_pos as the position represented + // by m_iMaxPosOff, as if !extended_end, it was unchanged. + else if (newpktpos == m_iEndPos) + { + // Case [D]: inserted a packet at the first gap following the + // contiguous region. This makes a potential to extend the + // contiguous region and we need to find its end. + + // If insertion happened at the very first packet, it is the + // new earliest packet now. In any other situation under this + // condition there's some contiguous packet range preceding + // this position. + if (m_iEndPos == m_iStartPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + + updateGapInfo(prev_max_pos); + } + // XXX Not sure if that's the best performant comparison + // What is meant here is that newpktpos is between + // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. + // CONSIDER: make m_iDropPos rather m_iDropOff, this will make + // this comparison a simple subtraction. Note that offset will + // have to be updated on every shift of m_iStartPos. + else if (cmpPos(newpktpos, m_iDropPos) < 0) + { + // Case [C]: the newly inserted packet precedes the + // previous earliest delivery position after drop, + // that is, there is now a "better" after-drop delivery + // candidate. + + // New position updated a valid packet on an earlier + // position than the drop position was before, although still + // following a gap. + // + // We know it because if the position has filled a gap following + // a valid packet, this preceding valid packet would be pointed + // by m_iDropPos, or it would point to some earlier packet in a + // contiguous series of valid packets following a gap, hence + // the above condition wouldn't be satisfied. + m_iDropPos = newpktpos; + + // If there's an inserted packet BEFORE drop-pos (which makes it + // a new drop-pos), while the very first packet is absent (the + // below condition), it means we have a new earliest-available + // packet. Otherwise we would have only a newly updated drop + // position, but still following some earlier contiguous range + // of valid packets - so it's earlier than previous drop, but + // not earlier than the earliest packet. + if (m_iStartPos == m_iEndPos) + { + earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); + } + } + // OTHERWISE: case [D] in which nothing is to be updated. + + return earlier_time; } void CRcvBuffer::updateGapInfo(int prev_max_pos) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 70099d355..f25528832 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -106,6 +106,8 @@ namespace srt // [D] [C] [B] [A] (insertion cases) // | (start) --- (end) ===[gap]=== (after-loss) ... (max-pos) | // +// See the CRcvBuffer::updatePosInfo method for detailed implementation. +// // WHEN INSERTING A NEW PACKET: // // If the incoming sequence maps to newpktpos that is: @@ -263,6 +265,10 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); + time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(int pos, int& w_span); + void getAvailInfo(InsertInfo& w_if); + /// Update the values of `m_iEndPos` and `m_iDropPos` in /// case when `m_iEndPos` was updated to a position of a /// nonempty cell. From d28e3b28f042235e627e7bd6b60c5611087b6c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 19 Feb 2024 16:44:39 +0100 Subject: [PATCH 116/517] Introduced SRTRUNSTATUS to cover all cases of srt_startup(). Fixed documentation. --- common/devel_util.h | 1 + docs/API/API-functions.md | 422 ++++++++++++++++++-------------------- srtcore/api.cpp | 14 +- srtcore/api.h | 2 +- srtcore/core.h | 2 +- srtcore/srt.h | 13 +- srtcore/srt_c_api.cpp | 2 +- srtcore/udt.h | 2 +- 8 files changed, 217 insertions(+), 241 deletions(-) diff --git a/common/devel_util.h b/common/devel_util.h index 3c00ec356..b908cedc6 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -84,6 +84,7 @@ struct IntWrapperLoose: IntWrapper typedef IntWrapper SRTSOCKET; typedef IntWrapper SRTSTATUS; +typedef IntWrapper SRTRUNSTATUS; typedef IntWrapperLoose SRTSTATUS_LOOSE; diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 54895fe15..a14d4aa19 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -222,6 +222,33 @@ Since SRT v1.5.0. | | | +## Diagnostics and return types + +The SRT API functions usually report a status of the operation that they attempt to perform. +There are three general possibilities to report a success or failure, possibly with some +extra information: + +1. `SRTSTATUS` is usually an integer value with two possible variants: + * `SRT_STATUS_OK` (value: 0): the operation completed successfully + * `SRT_ERROR` (value: -1): the operation failed (see [`srt_getlasterror`](#srt_getlasterror)) + +2. `SRTSOCKET` can be returned by some of the functions, which can be: + * A positive value greater than 0, which is a valid Socket ID value + * `SRT_SOCKID_CONNREQ` for a success report when a Socket ID needs not be returned + * `SRT_INVALID_SOCK` for a failure report + +3. An value of type `int` that should be a positive value or 0 in case of a success, +and the value equal to `SRT_ERROR` (that is, -1) in case of failure. + +In the below function description, functions returning `SRTSTATUS` will not +have the provided return value description, as it always maches the one above. +For all other types the function-specific return value description will be provided. + +If the function returns `SRT_ERROR`, `SRT_INVALID_SOCK` or a value equal to -1 +in case of returning an `int` value, additional error code can be obtained +through the [`srt_getlasterror`](#srt_getlasterror) call. Possible codes for a +particular function are listed in the **Errors** table. + ## Library Initialization @@ -231,7 +258,7 @@ Since SRT v1.5.0. ### srt_startup ``` -int srt_startup(void); +SRTRUNSTATUS srt_startup(void); ``` This function shall be called at the start of an application that uses the SRT @@ -242,10 +269,10 @@ relying on this behavior is strongly discouraged. | Returns | | |:----------------------------- |:--------------------------------------------------------------- | -| 0 | Successfully run, or already started | -| 1 | This is the first startup, but the GC thread is already running | -| -1 | Failed | -| | | +| `SRT_RUN_OK` (0) | Successfully started | +| `SRT_RUN_ALREADY` (1) | The GC thread is already running or it was called once already | +| `SRT_RUN_ERROR` (-1) | Failed | +| | | | Errors | | |:----------------------------- |:--------------------------------------------------------------- | @@ -259,7 +286,7 @@ relying on this behavior is strongly discouraged. ### srt_cleanup ``` -int srt_cleanup(void); +SRTSTATUS srt_cleanup(void); ``` This function cleans up all global SRT resources and shall be called just before @@ -267,10 +294,8 @@ exiting the application that uses the SRT library. This cleanup function will st be called from the C++ global destructor, if not called by the application, although relying on this behavior is strongly discouraged. -| Returns | | -|:----------------------------- |:--------------------------------------------------------------- | -| 0 | A possibility to return other values is reserved for future use | -| | | +Currently this function can only return `SRT_STATUS_OK` and a possibility to return +`SRT_ERROR` is reserved for future use. **IMPORTANT**: Note that the startup/cleanup calls have an instance counter. This means that if you call [`srt_startup`](#srt_startup) multiple times, you need to call the @@ -335,11 +360,11 @@ Note that socket IDs always have the `SRTGROUP_MASK` bit clear. |:----------------------------- |:------------------------------------------------------- | | Socket ID | A valid socket ID on success | | `SRT_INVALID_SOCK` | (`-1`) on error | -| | | +| | | -| Errors | | -|:----------------------------- |:------------------------------------------------------------ | -| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources . | +| Errors | | +|:----------------------------- |:-------------------------------------------------- | +| [`SRT_ENOBUF`](#srt_enobuf) | Not enough memory to allocate required resources | | | | **NOTE:** This is probably a design flaw (:warning:   **BUG?**). Usually underlying system @@ -353,7 +378,7 @@ errors are reported by [`SRT_ECONNSETUP`](#srt_econnsetup). ### srt_bind ``` -int srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSTATUS srt_bind(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Binds a socket to a local address and port. Binding specifies the local network @@ -361,10 +386,11 @@ interface and the UDP port number to be used for the socket. When the local address is a wildcard (`INADDR_ANY` for IPv4 or `in6addr_any` for IPv6), then it's bound to all interfaces. -**IMPORTANT**: When you bind an IPv6 wildcard address, note that the -`SRTO_IPV6ONLY` option must be set on the socket explicitly to 1 or 0 prior to -calling this function. See -[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) for more details. +**IMPORTANT**: In the case of IPv6 wildcard address, this may mean either "all +IPv6 interfaces" or "all IPv4 and IPv6 interfaces", depending on the value of +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option. Therefore this +option must be explicitly set to 0 or 1 prior to calling this function, otherwise +(when the default -1 value of this option is left) this function will fail. Binding is necessary for every socket to be used for communication. If the socket is to be used to initiate a connection to a listener socket, which can be done, @@ -411,7 +437,7 @@ binding ("shared binding") is possessed by an SRT socket created in the same application, and: * Its binding address and UDP-related socket options match the socket to be bound. -* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDRS) is set to *true* (default). +* Its [`SRTO_REUSEADDR`](API-socket-options.md#SRTO_REUSEADDR) is set to *true* (default). If none of the free, side and shared binding options is currently possible, this function will fail. If the socket blocking the requested endpoint is an SRT @@ -419,14 +445,15 @@ socket in the current application, it will report the `SRT_EBINDCONFLICT` error, while if it was another socket in the system, or the problem was in the system in general, it will report `SRT_ESOCKFAIL`. Here is the table that shows possible situations: -| Requested binding | vs. Existing bindings... | | | | | -|---------------------|------------------------------|-----------|-----------------------------|---------------|---------------| -| | A.B.C.D | 0.0.0.0 | ::X | :: / V6ONLY=1 | :: / V6ONLY=0 | -| 1.2.3.4 | 1.2.3.4 shareable, else free | blocked | free | free | blocked | -| 0.0.0.0 | blocked | shareable | free | free | blocked | -| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | -| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | -| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | +| Requested binding | vs. Existing bindings... | | | | | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| | A.B.C.D (explicit IPv4 addr.) | 0.0.0.0 | ::X (explicit IPv6 addr.) | :: / V6ONLY=1 | :: / V6ONLY=0 | +|---------------------|---------------------------------|-----------|-----------------------------|---------------|---------------| +| 1.2.3.4 | shareable if 1.2.3.4, else free | blocked | free | free | blocked | +| 0.0.0.0 | blocked | shareable | free | free | blocked | +| 8080::1 | free | free | 8080::1 sharable, else free | blocked | blocked | +| :: / V6ONLY=1 | free | free | blocked | sharable | blocked | +| :: / V6ONLY=0 | blocked | blocked | blocked | blocked | sharable | Where: @@ -436,7 +463,7 @@ Where: * shareable: This binding can be shared with the requested binding if it's compatible. -* (ADDRESS) shareable, else free: this binding is shareable if the existing binding address is +* shareable if (ADDRESS), else free: this binding is shareable if the existing binding address is equal to the requested ADDRESS. Otherwise it's free. If the binding is shareable, then the operation will succeed if the socket that currently @@ -455,13 +482,9 @@ or set the appropriate source address using **IMPORTANT information about IPv6**: If you are going to bind to the `in6addr_any` IPv6 wildcard address (known as `::`), the `SRTO_IPV6ONLY` option must be first set explicitly to 0 or 1, otherwise the binding -will fail. In all other cases this option is meaningless. See `SRTO_IPV6ONLY` -option for more information. - -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) on error, otherwise 0 | -| | | +will fail. In all other cases this option is meaningless. See +[`SRTO_IPV6ONLY`](API-socket-options.md#SRTO_IPV6ONLY) option for more +information. | Errors | | |:---------------------------------------- |:-------------------------------------------------------------------- | @@ -481,7 +504,7 @@ option for more information. ### srt_bind_acquire ``` -int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); +SRTSTATUS srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock); ``` A version of [`srt_bind`](#srt_bind) that acquires a given UDP socket instead of creating one. @@ -524,7 +547,7 @@ Gets the current status of the socket. Possible states are: ### srt_getsndbuffer ``` -int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); +SRTSTATUS srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes); ``` Retrieves information about the sender buffer. @@ -546,18 +569,13 @@ socket needs to be closed asynchronously. ### srt_close ``` -int srt_close(SRTSOCKET u); +SRTSTATUS srt_close(SRTSOCKET u); ``` Closes the socket or group and frees all used resources. Note that underlying UDP sockets may be shared between sockets, so these are freed only with the last user closed. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------- |:----------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -585,7 +603,7 @@ last user closed. ### srt_listen ``` -int srt_listen(SRTSOCKET u, int backlog); +SRTSTATUS srt_listen(SRTSOCKET u, int backlog); ``` This sets up the listening state on a socket with a backlog setting that @@ -600,11 +618,6 @@ be called before [`srt_accept`](#srt_accept) can happen * [`SRTO_GROUPCONNECT`](API-socket-options.md#SRTO_GROUPCONNECT) option allows the listener socket to accept group connections -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0. | -| | | - | Errors | | |:--------------------------------------- |:-------------------------------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | Value of `backlog` is 0 or negative. | @@ -721,7 +734,7 @@ calling this function. | Returns | | |:----------------------------- |:---------------------------------------------------------------------- | -| SRT socket
group ID | On success, a valid SRT socket or group ID to be used for transmission | +| SRT socket/group ID | On success, a valid SRT socket or group ID to be used for transmission | | `SRT_INVALID_SOCK` | (-1) on failure | | | | @@ -741,7 +754,7 @@ calling this function. ### srt_listen_callback ``` -int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a socket that is @@ -755,12 +768,6 @@ connection has been accepted. * `hook_fn`: The callback hook function pointer (or NULL to remove the callback) * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:---------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - | Errors | | |:--------------------------------- |:----------------------------------------- | | [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| @@ -826,7 +833,7 @@ database you have to check against the data received in `streamid` or `peeraddr` ### srt_connect ``` -int srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); +SRTSOCKET srt_connect(SRTSOCKET u, const struct sockaddr* name, int namelen); ``` Connects a socket or a group to a remote party with a specified address and port. @@ -844,8 +851,8 @@ Connects a socket or a group to a remote party with a specified address and port or binding and connection can be done in one function ([`srt_connect_bind`](#srt_connect_bind)), such that it uses a predefined network interface or local outgoing port. This is optional in the case of a caller-listener arrangement, but obligatory for a rendezvous arrangement. -If not used, the binding will be done automatically to `INADDR_ANY` (which binds on all -interfaces) and port 0 (which makes the system assign the port automatically). +If not used, the binding will be done automatically to a wildcard address and port 0. See +[`srt_bind](#srt_bind) for details. 2. This function is used for both connecting to the listening peer in a caller-listener arrangement, and calling the peer in rendezvous mode. For the latter, the @@ -861,16 +868,21 @@ automatically for every call of this function. mode, you might want to use [`srt_connect_group`](#srt_connect_group) instead. This function also allows you to use additional settings, available only for groups. +The returned value is a socket ID value. When `u` is a socket ID, the returned +is a special value `SRT_SOCKID_CONNREQ`. When `u` is a group ID, the returned +value is the socket ID of the newly created member for the requested link. In +the case of failure, `SRT_INVALID_SOCK` is returned. + | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | | Errors | | |:------------------------------------- |:----------------------------------------------------------- | -| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket or group ID | | [`SRT_ERDVUNBOUND`](#srt_erdvunbound) | Socket [`u`](#u) is in rendezvous mode, but it wasn't bound (see note #2) | | [`SRT_ECONNSOCK`](#srt_econnsock) | Socket [`u`](#u) is already connected | | [`SRT_ECONNREJ`](#srt_econnrej) | Connection has been rejected | @@ -895,7 +907,9 @@ In the case of "late" failures you can additionally call information. Note that in blocking mode only for the `SRT_ECONNREJ` error this function may return any additional information. In non-blocking mode a detailed "late" failure cannot be distinguished, and therefore it -can also be obtained from this function. +can also be obtained from this function. Note that the connection timeout +error can be also recognized through this call, even though it is reported +by `SRT_ENOSERVER` in the blocking mode. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -905,7 +919,7 @@ can also be obtained from this function. ### srt_connect_bind ``` -int srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, +SRTSOCKET srt_connect_bind(SRTSOCKET u, const struct sockaddr* source, const struct sockaddr* target, int len); ``` @@ -923,8 +937,8 @@ first on the automatically created socket for the connection. | Returns | | |:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error | -| 0 | In case when used for [`u`](#u) socket | +| `SRT_INVALID_SOCK` | (-1) in case of error | +| `SRT_SOCKID_CONNREQ` | In case when used for [`u`](#u) socket | | Socket ID | Created for connection for [`u`](#u) group | | | | @@ -953,7 +967,7 @@ different families (that is, both `source` and `target` must be `AF_INET` or ### srt_connect_debug ``` -int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); +SRTSOCKET srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn); ``` This function is for developers only and can be used for testing. It does the @@ -968,7 +982,7 @@ is generated randomly. ### srt_rendezvous ``` -int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, +SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen); ``` Performs a rendezvous connection. This is a shortcut for doing bind locally, @@ -981,11 +995,6 @@ to true, and doing [`srt_connect`](#srt_connect). * `local_name`: specifies the local network interface and port to bind * `remote_name`: specifies the remote party's IP address and port -| Returns | | -|:----------------------------- |:-------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:------------------------------------- |:-------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket passed as [`u`](#u) designates no valid socket | @@ -1007,7 +1016,7 @@ allowed (that is, both `local_name` and `remote_name` must be `AF_INET` or `AF_I ### srt_connect_callback ``` -int srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); +SRTSTATUS srt_connect_callback(SRTSOCKET u, srt_connect_callback_fn* hook_fn, void* hook_opaque); ``` This call installs a callback hook, which will be executed on a given [`u`](#u) @@ -1036,16 +1045,10 @@ connection failures. * `hook_opaque`: The pointer value that will be passed to the callback function -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Successful | -| -1 | Error | -| | | - -| Errors | | -|:---------------------------------- |:------------------------------------------| -| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket| -| | | +| Errors | | +|:---------------------------------- |:-------------------------------------------| +| [`SRT_ECONNSOCK`](#srt_econnsock) | It can't be modified in a connected socket | +| | | The callback function signature has the following type definition: @@ -1117,7 +1120,7 @@ where: * `token`: An integer value unique for every connection, or -1 if unused The `srt_prepare_endpoint` sets these fields to default values. After that -you can change the value of `weight` and `config` and `token` fields. The +you can change the value of `weight`, `config` and `token` fields. The `weight` parameter's meaning is dependent on the group type: * BROADCAST: not used @@ -1261,10 +1264,12 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| `SRTSOCKET` | Corresponding group SRT socket ID of the member socket. | +| `SRTSOCKET` | Corresponding group SRT socket ID of the `member` socket. | | `SRT_INVALID_SOCK` | The socket doesn't exist, it is not a member of any group, or bonding API is disabled. | | | | +In the case of `SRT_INVALID_SOCK`, the error is set to `SRT_EINVPARAM`. + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1273,7 +1278,7 @@ Retrieves the group SRT socket ID that corresponds to the member socket ID `memb #### srt_group_data ``` -int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); +SRTSTATUS srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* inoutlen); ``` **Arguments**: @@ -1284,23 +1289,26 @@ int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA output[], size_t* in and is set to the filled array's size This function obtains the current member state of the group specified in -`socketgroup`. The `output` should point to an array large enough to hold all -the elements. The `inoutlen` should point to a variable initially set to the size -of the `output` array. The current number of members will be written back to `inoutlen`. +`socketgroup`. -If the size of the `output` array is enough for the current number of members, -the `output` array will be filled with group data and the function will return -the number of elements filled. Otherwise the array will not be filled and -`SRT_ERROR` will be returned. +The `inoutlen` should point to a variable initially set to the size +of the `output` array. The current number of members will be written back to +the variable specified in `inoutlen`. This paramterer cannot be NULL. -This function can be used to get the group size by setting `output` to `NULL`, -and providing `socketgroup` and `inoutlen`. +If `output` is specified and the size of the array is at least equal to the +number of group members, the `output` array will be filled with group data. -| Returns | | -|:----------------------------- |:-------------------------------------------------- | -| # of elements | The number of data elements filled, on success | -| -1 | Error | -| | | +If `output` is NULL then the function will only retrieve the number of elements +in `inoutlen`. + +This call will fail and return `SRT_ERROR` if: + +* The `socketgroup` parameter is invalid + +* The `inoutlen` parameter is NULL + +* The size specified in a variable passed via `inoutlen` is less than the number +of group members | Errors | | @@ -1310,13 +1318,14 @@ and providing `socketgroup` and `inoutlen`. | | | -| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | -|:---------:|:--------------:|:------------:|:----------:|:------------:|:---------------------------------:| -| NULL | NULL | -1 | NULL | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| NULL | ptr | 0 | NULL | group.size() | ✖️ | -| ptr | NULL | -1 | ✖️ | NULL | [`SRT_EINVPARAM`](#srt_einvparam) | -| ptr | ≥ group.size | group.size() | group.data | group.size | ✖️ | -| ptr | < group.size | -1 | ✖️ | group.size | [`SRT_ELARGEMSG`](#srt_elargemsg) | + +| in:output | in:inoutlen | returns | out:output | out:inoutlen | Error | +|:---------:|:--------------:|:---------------:|:----------:|:------------:|:---------------------------------:| +| ptr | ≥ group.size | `SRT_STATUS_OK` | group.data | group.size() | ✖️ | +| NULL | ptr | `SRT_STATUS_OK` | (unused) | group.size() | ✖️ | +| NULL | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | NULL | `SRT_ERROR` | (unused) | (not filled) | [`SRT_EINVPARAM`](#srt_einvparam) | +| ptr | < group.size | `SRT_ERROR` | (unused) | group.size() | [`SRT_ELARGEMSG`](#srt_elargemsg) | [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -1326,14 +1335,14 @@ and providing `socketgroup` and `inoutlen`. #### srt_connect_group ``` -int srt_connect_group(SRTSOCKET group, - SRT_SOCKGROUPCONFIG name [], int arraysize); +SRTSOCKET srt_connect_group(SRTSOCKET group, + SRT_SOCKGROUPCONFIG links [], int arraysize); ``` This function does almost the same as calling [`srt_connect`](#srt_connect) or [`srt_connect_bind`](#srt_connect_bind) (when the source was specified for [`srt_prepare_endpoint`](#srt_prepare_endpoint)) in a loop for every item specified -in the `name` array. However if blocking mode is being used, the first call to +in the `links` array. However if blocking mode is being used, the first call to [`srt_connect`](#srt_connect) would block until the connection is established, whereas this function blocks until any of the specified connections is established. @@ -1342,21 +1351,21 @@ option), there's no difference, except that the [`SRT_SOCKGROUPCONFIG`](#SRT_SOC structure allows adding extra configuration data used by groups. Note also that this function accepts only groups, not sockets. -The elements of the `name` array need to be prepared with the use of the +The elements of the `links` array need to be prepared with the use of the [`srt_prepare_endpoint`](#srt_prepare_endpoint) function. Note that it is **NOT** required that every target address specified is of the same family. Return value and errors in this function are the same as in [`srt_connect`](#srt_connect), although this function reports success when at least one connection has succeeded. If none has succeeded, this function reports an [`SRT_ECONNLOST`](#srt_econnlost) -error. Particular connection states can be obtained from the `name` -array upon return from the [`errorcode`](#error-codes) field. +error. Particular connection states can be obtained from the `links` array upon +return from the [`errorcode`](#error-codes) field. The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the following meaning: **Input**: -* `id`: unused, should be -1 (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) +* `id`: unused, should be `SRT_INVALID_SOCK` (default when created by [`srt_prepare_endpoint`](#srt_prepare_endpoint)) * `srcaddr`: address to bind before connecting, if specified (see below for details) * `peeraddr`: target address to connect * `weight`: weight value to be set on the link @@ -1366,7 +1375,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f **Output**: -* `id`: The socket created for that connection (-1 if failed to create) +* `id`: The socket created for that connection (`SRT_INVALID_SOCK` if failed to create) * `srcaddr`: unchanged * `peeraddr`: unchanged * `weight`: unchanged @@ -1374,10 +1383,11 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f * [`errorcode`](#error-codes): status of connection for that link ([`SRT_SUCCESS`](#srt_success) if succeeded) * `token`: same as in input, or a newly created token value if input was -1 + | Returns | | |:----------------------------- |:-------------------------------------------------- | -| `SRT_SOCKET` | The socket ID of the first connected member. | -| -1 | Error | +| Socket ID | The socket ID of the first connected member. | +| `SRT_INVALID_SOCK` | Error | | | | @@ -1388,7 +1398,7 @@ The fields of [`SRT_SOCKGROUPCONFIG`](#SRT_SOCKGROUPCONFIG) structure have the f | | | The procedure of connecting for every connection definition specified -in the `name` array is performed the following way: +in the `links` array is performed the following way: 1. The socket for this connection is first created @@ -1415,7 +1425,8 @@ then, and for which the connection attempt has at least successfully started, remain group members, although the function will return immediately with an error status (that is, without waiting for the first successful connection). If your application wants to do any partial recovery from this situation, it can -only use the epoll mechanism to wait for readiness. +only check the current member status via [`srt_group_data`](#srt_group_data) +and wait for group's write readiness (`SRT_EPOLL_OUT`) by using epoll. 2. In any other case, if an error occurs at any stage of the above process, the processing is interrupted for this very array item only, the socket used for it @@ -1423,16 +1434,18 @@ is immediately closed, and the processing of the next elements continues. In the of a connection process, it also passes two stages - parameter check and the process itself. Failure at the parameter check breaks this process, while if the check passes, this item is considered correctly processed, even if the connection -attempt is going to fail later. If this function is called in blocking mode, -it then blocks until at least one connection reports success, or if all of them -fail. The status of connections that continue in the background after this function -exits can then be checked by [`srt_group_data`](#srt_group_data). +attempt is going to fail later. + +If this function is called in blocking mode, it then blocks until at least one +connection reports success, or if all of them fail. The status of connections +that continue in the background after this function exits can then be checked +by [`srt_group_data`](#srt_group_data). As member socket connections are running in the background, for determining if a particular connection has succeeded or failed it is recommended to use [`srt_connect_callback`](#srt_connect_callback). In this case the `token` callback function parameter will be the same as the `token` value used -for the particular item in the `name` connection table. +for the particular item in the `links` connection array. The `token` value doesn't have any limitations except that the -1 value is a "trap representation", that is, when set on input it will make the internals @@ -1534,7 +1547,7 @@ Deletes the configuration object. #### srt_config_add ``` -int srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); +SRTSTATUS srt_config_add(SRT_SOCKOPT_CONFIG* c, SRT_SOCKOPT opt, void* val, int len); ``` Adds a configuration option to the configuration object. @@ -1564,12 +1577,6 @@ The following options are allowed to be set on the member socket: * [`SRTO_UDP_SNDBUF`](API-socket-options.md#SRTO_UDP_SNDBUF): UDP sender buffer, if this link has a big flight window -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:---------------------------------- |:--------------------------------------------------------------------- | | [`SRT_EINVPARAM`](#srt_einvparam) | This option is not allowed to be set on a socket being a group member. Or if bonding API is disabled. | @@ -1595,15 +1602,11 @@ The following options are allowed to be set on the member socket: ### srt_getpeername ``` -int srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Retrieves the remote address to which the socket is connected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:------------------------------------------------------------------------ | @@ -1618,7 +1621,7 @@ Retrieves the remote address to which the socket is connected. ### srt_getsockname ``` -int srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); +SRTSTATUS srt_getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen); ``` Extracts the address to which the socket was bound. Although you should know @@ -1627,10 +1630,6 @@ useful for extracting the local outgoing port number when it was specified as 0 with binding for system autoselection. With this function you can extract the port number after it has been autoselected. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | | Errors | | |:------------------------------- |:---------------------------------------------- | @@ -1659,8 +1658,8 @@ if (res < 0) { ### srt_getsockflag ```c++ -int srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); -int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); +SRTSTATUS srt_getsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, void* optval, int* optlen); +SRTSTATUS srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); ``` Gets the value of the given socket option (from a socket or a group). @@ -1678,11 +1677,6 @@ For most options, it will be the size of an integer. Some options, however, use The application is responsible for allocating sufficient memory space as defined and pointed to by `optval`. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:-------------------------------- |:---------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1697,8 +1691,8 @@ The application is responsible for allocating sufficient memory space as defined ### srt_setsockflag ```c++ -int srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); -int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRTSTATUS srt_setsockopt(SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT opt, const void* optval, int optlen); +SRTSTATUS srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); ``` Sets a value for a socket option in the socket or group. @@ -1715,11 +1709,6 @@ Please note that some of the options can only be set on sockets or only on groups, although most of the options can be set on the groups so that they are then derived by the member sockets. -| Returns | | -|:----------------------------- |:----------------------------------------------- | -| `SRT_ERROR` | (-1) in case of error, otherwise 0 | -| | | - | Errors | | |:----------------------------------- |:--------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | @@ -1728,7 +1717,7 @@ are then derived by the member sockets. | [`SRT_ECONNSOCK`](#srt_econnsock) | Tried to set an option with PRE_BIND or PRE restriction on a socket in connecting/listening/connected state. | | | | -**NOTE*: Various other errors may result from problems when setting a +**NOTE**: Various other errors may result from problems when setting a specific option (see option description in [API-socket-options.md](./API-socket-options.md) for details). @@ -1743,7 +1732,7 @@ uint32_t srt_getversion(); ``` Get SRT version value. The version format in hex is 0xXXYYZZ for x.y.z in human -readable form, where x = ("%d", (version>>16) & 0xff), etc. +readable form. E.g. 0x012033 means version 1.20.33. | Returns | | |:----------------------------- |:--------------------------------------------------------- | @@ -1911,7 +1900,7 @@ single call to this function determines a message's boundaries. |:----------------------------- |:--------------------------------------------------------- | | Size | Size of the data sent, if successful | | `SRT_ERROR` | In case of error (-1) | -| | | +| | | **NOTE**: Note that in **file/stream mode** the returned size may be less than `len`, which means that it didn't send the whole contents of the buffer. You would need to @@ -1990,10 +1979,10 @@ the currently lost one, it will be delivered and the lost one dropped. | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | Size (\>0) of the data received, if successful. | +| Size value \> 0 | Size of the data received, if successful. | | 0 | If the connection has been closed | | `SRT_ERROR` | (-1) when an error occurs | -| | | +| | | | Errors | | |:--------------------------------------------- |:--------------------------------------------------------- | @@ -2046,7 +2035,7 @@ You need to pass them to the [`srt_sendfile`](#srt_sendfile) or | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| Size | The size (\>0) of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | +| Size value \> 0 | The size of the transmitted data of a file. It may be less than `size`, if the size was greater
than the free space in the buffer, in which case you have to send rest of the file next time. | | -1 | in case of error | | | | @@ -2090,10 +2079,10 @@ the required range already, so for a numbers like 0x7FFFFFF0 and 0x10, for which ### srt_bistats ``` // Performance monitor with Byte counters for better bitrate estimation. -int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); +SRTSTATUS srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear); // Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes. -int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); +SRTSTATUS srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous); ``` Reports the current statistics @@ -2109,12 +2098,6 @@ Reports the current statistics of the fields please refer to [SRT Statistics](statistics.md). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Invalid socket ID provided. @@ -2167,11 +2150,11 @@ function can then be used to block until any readiness status in the whole int srt_epoll_create(void); ``` -Creates a new epoll container. +Creates a new epoll container and returns its identifier (EID). | Returns | | |:----------------------------- |:--------------------------------------------------------- | -| valid EID | Success | +| valid EID >= 0 | Success | | -1 | Failure | | | | @@ -2191,10 +2174,10 @@ Creates a new epoll container. ### srt_epoll_update_ssock ``` -int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); -int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); -int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events); +SRTSTATUS srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events); +SRTSTATUS srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events); ``` Adds a socket to a container, or updates an existing socket subscription. @@ -2274,12 +2257,6 @@ as level-triggered, you can do two separate subscriptions for the same socket. any possible flag, you must use [`srt_epoll_uwait`](#srt_epoll_uwait). Note that this function doesn't work with system file descriptors. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2298,8 +2275,8 @@ the [`SRT_ECONNSETUP`](#srt_econnsetup) code is predicted. ### srt_epoll_remove_ssock ``` -int srt_epoll_remove_usock(int eid, SRTSOCKET u); -int srt_epoll_remove_ssock(int eid, SYSSOCKET s); +SRTSTATUS srt_epoll_remove_usock(int eid, SRTSOCKET u); +SRTSTATUS srt_epoll_remove_ssock(int eid, SYSSOCKET s); ``` Removes a specified socket from an epoll container and clears all readiness @@ -2308,12 +2285,6 @@ states recorded for that socket. The `_usock` suffix refers to a user socket (SRT socket). The `_ssock` suffix refers to a system socket. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2372,7 +2343,7 @@ the only way to know what kind of error has occurred on the socket. | Returns | | |:----------------------------- |:------------------------------------------------------------ | -| Number | The number (\>0) of ready sockets, of whatever kind (if any) | +| Number \> 0 | The number of ready sockets, of whatever kind (if any) | | -1 | Error | | | | @@ -2411,7 +2382,7 @@ indefinitely until a readiness state occurs. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------- | -| Number | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | +| Number \> 0 | The number of user socket (SRT socket) state changes that have been reported in `fdsSet`,
if this number isn't greater than `fdsSize` | | `fdsSize` + 1 | This means that there was not enough space in the output array to report all events.
For events subscribed with the [`SRT_EPOLL_ET`](#SRT_EPOLL_ET) flag only those will be cleared that were reported.
Others will wait for the next call. | | 0 | If no readiness state was found on any socket and the timeout has passed
(this is not possible when waiting indefinitely) | | -1 | Error | @@ -2420,8 +2391,14 @@ indefinitely until a readiness state occurs. | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | -| [`SRT_EINVPARAM`](#srt_einvparam) | One of possible usage errors:
* `fdsSize` is < 0
* `fdsSize` is > 0 and `fdsSet` is a null pointer
* [`eid`](#eid) was subscribed to any system socket | -| | | +| [`SRT_EINVPARAM`](#srt_einvparam) | Usage error (see below) | +| | | + +Usage errors reported as `SRT_EINVPARAM`: + +* `fdsSize` is \< 0 +* `fdsSize` is \> 0 and `fdsSet` is a null pointer +* [`eid`](#eid) was subscribed to any system socket **IMPORTANT**: This function reports timeout by returning 0, not by [`SRT_ETIMEOUT`](#srt_etimeout) error. @@ -2451,18 +2428,12 @@ closed and its state can be verified with a call to [`srt_getsockstate`](#srt_ge ### srt_epoll_clear_usocks ``` -int srt_epoll_clear_usocks(int eid); +SRTSTATUS srt_epoll_clear_usocks(int eid); ``` This function removes all SRT ("user") socket subscriptions from the epoll container identified by [`eid`](#eid). -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Success | -| -1 | Failure | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2504,7 +2475,7 @@ the general output array is not empty. | Returns | | |:----------------------------- |:-------------------------------------------------------------------------- | | | This function returns the state of the flags at the time before the call | -| -1 | Special value in case when an error occurred | +| `SRT_ERROR` (-1) | Special value in case when an error occurred | | | | | Errors | | @@ -2519,17 +2490,11 @@ the general output array is not empty. ### srt_epoll_release ``` -int srt_epoll_release(int eid); +SRTSTATUS srt_epoll_release(int eid); ``` Deletes the epoll container. -| Returns | | -|:----------------------------- |:-------------------------------------------------------------- | -| | The number (\>0) of ready sockets, of whatever kind (if any) | -| -1 | Error | -| | | - | Errors | | |:----------------------------------- |:----------------------------------------------------------------- | | [`SRT_EINVPOLLID`](#srt_einvpollid) | [`eid`](#eid) parameter doesn't refer to a valid epoll container | @@ -2745,7 +2710,7 @@ and `msTimeStamp` value of the `SRT_TRACEBSTATS` (see [SRT Statistics](statistic | Returns | | |:----------------------------- |:--------------------------------------------------------------------------- | | | Connection time in microseconds elapsed since epoch of SRT internal clock | -| -1 | Error | +| `SRT_ERROR` (-1) | Error | | | | | Errors | | @@ -2897,7 +2862,7 @@ The actual messages assigned to the internal rejection codes, that is, less than ### srt_setrejectreason ``` -int srt_setrejectreason(SRTSOCKET sock, int value); +SRTSTATUS srt_setrejectreason(SRTSOCKET sock, int value); ``` Sets the rejection code on the socket. This call is only useful in the listener @@ -2911,12 +2876,6 @@ can inform the calling side that the resource specified under the `r` key in the StreamID string (see [`SRTO_STREAMID`](API-socket-options.md#SRTO_STREAMID)) is not available - it then sets the value to `SRT_REJC_PREDEFINED + 404`. -| Returns | | -|:----------------------------- |:--------------------------------------------------------- | -| 0 | Error | -| -1 | Success | -| | | - | Errors | | |:--------------------------------- |:-------------------------------------------- | | [`SRT_EINVSOCK`](#srt_einvsock) | Socket `sock` is not an ID of a valid socket | @@ -2942,6 +2901,8 @@ used for the connection, the function should also be called when the a numeric code, which can be translated into a message by [`srt_rejectreason_str`](#srt_rejectreason_str). +The returned value is one of the values listed in enum `SRT_REJECT_REASON`. +For an invalid value of `sock` the `SRT_REJ_UNKNOWN` is returned. [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) @@ -2952,7 +2913,8 @@ a numeric code, which can be translated into a message by #### SRT_REJ_UNKNOWN -A fallback value for cases when there was no connection rejected. +A fallback value for cases when there was no connection rejected or the +reason cannot be obtained. #### SRT_REJ_SYSTEM @@ -3139,7 +3101,8 @@ is no longer usable. #### SRT_ECONNFAIL -General connection failure of unknown details. +General connection failure of unknown details (currently is not reported +directly by any API function and it's reserved for future use). #### SRT_ECONNLOST @@ -3357,34 +3320,37 @@ Example 1: * Socket 1: bind to IPv4 0.0.0.0 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = true -* Result: NOT intersecting +* Result: NOT intersecting, allowed to proceed Example 2: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv4 0.0.0.0 -* Result: intersecting (and conflicting) +* Result: failure: 0.0.0.0 encloses 1.2.3.4, so they are in conflict Example 3: * Socket 1: bind to IPv4 1.2.3.4 * Socket 2: bind to IPv6 :: with `SRTO_IPV6ONLY` = false -* Result: intersecting (and conflicting) +* Result: failure: this encloses all IPv4, so it conflicts with 1.2.3.4 -If any common range coverage is found between the attempted binding specification -(in `srt_bind` call) and the found existing binding with the same port number, -then all of the following conditions must be satisfied between them: +Binding another socket to an endpoint that is already bound by another +socket is possible, and results in a shared binding, as long as the binding +address that is enclosed by this existing binding is exactly identical to +the specified one and all of the following conditions must be satisfied between +them: 1. The `SRTO_REUSEADDR` must be true (default) in both. 2. The IP address specification (in case of IPv6, also including the value of `SRTO_IPV6ONLY` flag) must be exactly identical. -3. The UDP-specific settings must be identical. +3. The UDP-specific settings (SRT options that map to UDP options) must be identical. If any of these conditions isn't satisfied, the `srt_bind` function results in conflict and report this error. + #### SRT_EASYNCFAIL General asynchronous failure (not in use currently). diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 35252ada0..6b32efe16 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -236,12 +236,12 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) return os.str(); } -SRTSTATUS srt::CUDTUnited::startup() +SRTRUNSTATUS srt::CUDTUnited::startup() { ScopedLock gcinit(m_InitLock); if (m_iInstanceCount++ > 0) - return SRTSTATUS(1); + return SRT_RUN_ALREADY; // Global initialization code #ifdef _WIN32 @@ -258,18 +258,18 @@ SRTSTATUS srt::CUDTUnited::startup() PacketFilter::globalInit(); if (m_bGCStatus) - return SRTSTATUS(1); + return SRT_RUN_ALREADY; m_bClosing = false; if (!StartThread(m_GCThread, garbageCollect, this, "SRT:GC")) - return SRT_ERROR; + return SRT_RUN_ERROR; m_bGCStatus = true; HLOGC(inlog.Debug, log << "SRT Clock Type: " << SRT_SYNC_CLOCK_STR); - return SRT_STATUS_OK; + return SRT_RUN_OK; } SRTSTATUS srt::CUDTUnited::cleanup() @@ -3363,7 +3363,7 @@ void* srt::CUDTUnited::garbageCollect(void* p) //////////////////////////////////////////////////////////////////////////////// -SRTSTATUS srt::CUDT::startup() +SRTRUNSTATUS srt::CUDT::startup() { return uglobal().startup(); } @@ -4355,7 +4355,7 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) namespace UDT { -SRTSTATUS startup() +SRTRUNSTATUS startup() { return srt::CUDT::startup(); } diff --git a/srtcore/api.h b/srtcore/api.h index 287336b32..616b7840b 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -245,7 +245,7 @@ class CUDTUnited /// initialize the UDT library. /// @return 0 if success, otherwise -1 is returned. - SRTSTATUS startup(); + SRTRUNSTATUS startup(); /// release the UDT library. /// @return 0 if success, otherwise -1 is returned. diff --git a/srtcore/core.h b/srtcore/core.h index d72bb072e..d836a7775 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -190,7 +190,7 @@ class CUDT ~CUDT(); public: //API - static SRTSTATUS startup(); + static SRTRUNSTATUS startup(); static SRTSTATUS cleanup(); static SRTSOCKET socket(); #if ENABLE_BONDING diff --git a/srtcore/srt.h b/srtcore/srt.h index 682a6fe26..9b2e2c423 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -145,6 +145,7 @@ written by // This is normal and should be normally used. typedef int32_t SRTSOCKET; typedef int SRTSTATUS; +typedef int SRTRUNSTATUS; #else // Used for development only. #include "../common/devel_util.h" @@ -770,21 +771,29 @@ static const SRTSOCKET SRT_INVALID_SOCK (-1); static const SRTSOCKET SRT_SOCKID_CONNREQ (0); static const SRTSTATUS SRT_ERROR (-1); static const SRTSTATUS SRT_STATUS_OK (0); +static const SRTRUNSTATUS SRT_RUN_ERROR (-1); +static const SRTRUNSTATUS SRT_RUN_OK (0); +static const SRTRUNSTATUS SRT_RUN_ALREADY (1); -#else + +#else // C version static const SRTSOCKET SRT_INVALID_SOCK = -1; static const SRTSOCKET SRT_SOCKID_CONNREQ = 0; static const SRTSTATUS SRT_ERROR = -1; static const SRTSTATUS SRT_STATUS_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ERROR = -1; +static const SRTRUNSTATUS SRT_RUN_OK = 0; +static const SRTRUNSTATUS SRT_RUN_ALREADY = 1; #endif + typedef struct CBytePerfMon SRT_TRACEBSTATS; // library initialization -SRT_API SRTSTATUS srt_startup(void); +SRT_API SRTRUNSTATUS srt_startup(void); SRT_API SRTSTATUS srt_cleanup(void); // diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 011f277ae..de2605db7 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -29,7 +29,7 @@ using namespace srt; extern "C" { -SRTSTATUS srt_startup() { return CUDT::startup(); } +SRTRUNSTATUS srt_startup() { return CUDT::startup(); } SRTSTATUS srt_cleanup() { return CUDT::cleanup(); } // Socket creation. diff --git a/srtcore/udt.h b/srtcore/udt.h index c0e1a53ff..5cc63aa41 100644 --- a/srtcore/udt.h +++ b/srtcore/udt.h @@ -168,7 +168,7 @@ SRT_API extern const SRTSOCKET INVALID_SOCK; #undef ERROR SRT_API extern const SRTSTATUS ERROR; -SRT_API SRTSTATUS startup(); +SRT_API SRTRUNSTATUS startup(); SRT_API SRTSTATUS cleanup(); SRT_API SRTSOCKET socket(); inline SRTSOCKET socket(int , int , int ) { return socket(); } From be302ca330b4dda06acd3fa3dcbec841c3f8d0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 20 Feb 2024 10:26:21 +0100 Subject: [PATCH 117/517] Removed srt::isgroup. Reused CUDT::isgroup where applicable. Added missing license heading. --- common/devel_util.h | 28 ++++++++++++++++++++++++++++ srtcore/api.cpp | 28 ++++++++++++++-------------- srtcore/srt.h | 6 ------ srtcore/srt_c_api.cpp | 4 +++- testing/testmedia.cpp | 2 +- testing/testmedia.hpp | 2 +- 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/common/devel_util.h b/common/devel_util.h index b908cedc6..4e31af07e 100644 --- a/common/devel_util.h +++ b/common/devel_util.h @@ -1,3 +1,31 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// IMPORTANT!!! +// +// This is normally not a part of SRT source files. This is a developer utility +// that allows developers to perform a compile test on a version instrumented +// with type checks. To do that, you can do one of two things: +// +// - configure the compiling process with extra -DSRT_TEST_FORCED_CONSTANT=1 flag +// - unblock the commented out #define in srt.h file for that constant +// +// Note that there's no use of such a compiled code. This is done only so that +// the compiler can detect any misuses of the SRT symbolic type names and +// constants. + #include diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 6b32efe16..660a07251 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -874,7 +874,7 @@ SRTSTATUS srt::CUDTUnited::installConnectHook(const SRTSOCKET u, srt_connect_cal try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->installConnectHook(hook, opaq); @@ -1214,7 +1214,7 @@ SRTSOCKET srt::CUDTUnited::connect(SRTSOCKET u, const sockaddr* srcname, const s // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); // Note: forced_isn is ignored when connecting a group. @@ -1255,7 +1255,7 @@ SRTSOCKET srt::CUDTUnited::connect(const SRTSOCKET u, const sockaddr* name, int // Check affiliation of the socket. It's now allowed for it to be // a group or socket. For a group, add automatically a socket to // the group. - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); @@ -1894,7 +1894,7 @@ void srt::CUDTUnited::connectIn(CUDTSocket* s, const sockaddr_any& target_addr, SRTSTATUS srt::CUDTUnited::close(const SRTSOCKET u) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); k.group->close(); @@ -2352,7 +2352,7 @@ void srt::CUDTUnited::epoll_clear_usocks(int eid) void srt::CUDTUnited::epoll_add_usock(const int eid, const SRTSOCKET u, const int* events) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); m_EPoll.update_usock(eid, u, events); @@ -2430,7 +2430,7 @@ void srt::CUDTUnited::epoll_remove_usock(const int eid, const SRTSOCKET u) #if ENABLE_BONDING CUDTGroup* g = 0; - if (isgroup(u)) + if (CUDT::isgroup(u)) { GroupKeeper k(*this, u, ERH_THROW); g = k.group; @@ -3504,7 +3504,7 @@ SRTSOCKET srt::CUDT::getGroupOfSocket(SRTSOCKET socket) SRTSTATUS srt::CUDT::getGroupData(SRTSOCKET groupid, SRT_SOCKGROUPDATA* pdata, size_t* psize) { - if (!isgroup(groupid) || !psize) + if (!CUDT::isgroup(groupid) || !psize) { return APIError(MJ_NOTSUP, MN_INVAL, 0); } @@ -3675,7 +3675,7 @@ SRTSOCKET srt::CUDT::connectLinks(SRTSOCKET grp, SRT_SOCKGROUPCONFIG targets[], if (arraysize <= 0) return APIError(MJ_NOTSUP, MN_INVAL, 0), SRT_INVALID_SOCK; - if (!isgroup(grp)) + if (!CUDT::isgroup(grp)) { // connectLinks accepts only GROUP id, not socket id. return APIError(MJ_NOTSUP, MN_SIDINVAL, 0), SRT_INVALID_SOCK; @@ -3786,7 +3786,7 @@ SRTSTATUS srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_ try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->getOpt(optname, (pw_optval), (*pw_optlen)); @@ -3817,7 +3817,7 @@ SRTSTATUS srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const voi try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); k.group->setOpt(optname, optval, optlen); @@ -3862,7 +3862,7 @@ int srt::CUDT::sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->send(buf, len, (w_m)); @@ -3906,7 +3906,7 @@ int srt::CUDT::recvmsg2(SRTSOCKET u, char* buf, int len, SRT_MSGCTRL& w_m) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->recv(buf, len, (w_m)); @@ -4251,7 +4251,7 @@ srt::CUDTException& srt::CUDT::getlasterror() SRTSTATUS srt::CUDT::bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear, bool instantaneous) { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) return groupsockbstats(u, perf, clear); #endif @@ -4329,7 +4329,7 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) try { #if ENABLE_BONDING - if (isgroup(u)) + if (CUDT::isgroup(u)) { CUDTUnited::GroupKeeper k(uglobal(), u, CUDTUnited::ERH_THROW); return k.group->getStatus(); diff --git a/srtcore/srt.h b/srtcore/srt.h index 9b2e2c423..b18530257 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -164,12 +164,6 @@ extern "C" { // socket or a socket group. static const int32_t SRTGROUP_MASK = (1 << 30); -#ifdef __cplusplus -namespace srt { -inline bool isgroup(SRTSOCKET sid) { return (int32_t(sid) & SRTGROUP_MASK) != 0; } -} -#endif - #ifdef _WIN32 typedef SOCKET SYSSOCKET; #else diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index de2605db7..030d038b9 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -126,8 +126,10 @@ SRTSOCKET srt_connect_bind(SRTSOCKET u, SRTSTATUS srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen, const struct sockaddr* remote_name, int remote_namelen) { - if (isgroup(u)) +#if ENABLE_BONDING + if (CUDT::isgroup(u)) return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); +#endif bool yes = 1; CUDT::setsockopt(u, 0, SRTO_RENDEZVOUS, &yes, sizeof yes); diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index a5c47928e..0b99835bf 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -570,7 +570,7 @@ void SrtCommon::AcceptNewClient() } #if ENABLE_BONDING - if (srt::isgroup(m_sock)) + if (int32_t(m_sock) & SRTGROUP_MASK) { m_listener_group = true; if (m_group_config != "") diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index f7c49ddf6..5441c4d56 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -142,7 +142,7 @@ class SrtCommon void Acquire(SRTSOCKET s) { m_sock = s; - if (srt::isgroup(s)) + if (int32_t(s) & SRTGROUP_MASK) m_listener_group = true; } From ed1e8d0fa8a7163a94a57201c62d082c48efe4fb Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 21 Feb 2024 09:39:16 +0100 Subject: [PATCH 118/517] Update srtcore/api.h Co-authored-by: Maxim Sharabayko --- srtcore/api.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/srtcore/api.h b/srtcore/api.h index 1f2959c00..6c7fcb088 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -263,6 +263,9 @@ class CUDTUnited /// finished and there are no more users of this socket. /// Note that the swiped socket is no longer dispatchable /// by id. + /// @param id socket ID to swipe. + /// @param s pointer to the socket to swipe. + /// @param action only add to closed list or remove completely void swipeSocket_LOCKED(SRTSOCKET id, CUDTSocket* s, SwipeSocketTerm); /// Create (listener-side) a new socket associated with the incoming connection request. From 283021efbaca001e3b0f0de8fdea51b8de173ff9 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Wed, 28 Feb 2024 11:03:46 +0100 Subject: [PATCH 119/517] Apply suggestions from code review (further impossible fixes pending) Co-authored-by: Maxim Sharabayko --- srtcore/buffer_rcv.cpp | 1 - srtcore/buffer_rcv.h | 8 ++++---- srtcore/core.cpp | 4 ++-- srtcore/core.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 97b76d755..6d6ebd841 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -656,7 +656,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pairstatus_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) // // thread safety: -// start_pos_: CUDT::m_RecvLock +// m_iStartPos: CUDT::m_RecvLock // first_unack_pos_: CUDT::m_AckLock -// max_pos_inc_: none? (modified on add and ack -// first_nonread_pos_: +// m_iMaxPosOff: none? (modified on add and ack +// m_iFirstNonreadPos: // // // m_iStartPos: the first packet that should be read (might be empty) @@ -455,7 +455,7 @@ class CRcvBuffer return m_iMaxPosOff; } - // Unfortunately it still needs locking. + // Returns true if the buffer is full. Requires locking. bool full() const { return size() == capacity(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eaea48102..9afa74c9b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5544,7 +5544,7 @@ void * srt::CUDT::tsbpd(void* param) tsNextDelivery = steady_clock::time_point(); // Ready to read, nothing to wait for. } - SRT_ATR_UNUSED bool wakeup_on_signal = true; + SRT_ATR_UNUSED bool bWakeupOnSignal = true; // We may just briefly unlocked the m_RecvLock, so we need to check m_bClosing again to avoid deadlock. if (self->m_bClosing) @@ -8183,7 +8183,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) } } #endif - // Signalling m_RecvDataCond is not dane when TSBPD is on. + // Signalling m_RecvDataCond is not done when TSBPD is on. // This signalling is done in file mode in order to keep the // API reader thread sleeping until there is a "bigger portion" // of data to read. In TSBPD mode this isn't done because every diff --git a/srtcore/core.h b/srtcore/core.h index 8cff2fbce..dee7bd48d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -987,7 +987,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - sync::atomic m_bWakeOnRecv; // Expected to be woken up when received a packet + sync::atomic m_bWakeOnRecv; // Expected to wake up TSBPD when a read-ready data packet is received. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; From b969a8329e9275d6df2f8979a4b803de7499eaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 28 Feb 2024 12:06:29 +0100 Subject: [PATCH 120/517] Remaining post-review fixes --- srtcore/buffer_rcv.h | 5 +-- srtcore/core.cpp | 83 ++++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 93b51c2c8..24f5066f6 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -338,9 +338,10 @@ class CRcvBuffer /// Read the whole message from one or several packets. /// - /// @param [in,out] data buffer to write the message into. + /// @param [out] data buffer to write the message into. /// @param [in] len size of the buffer. - /// @param [in,out] message control data + /// @param [out,opt] message control data to be filled + /// @param [out,opt] pw_seqrange range of sequence numbers for packets belonging to the message /// /// @return actual number of bytes extracted from the buffer. /// 0 if nothing to read. diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9afa74c9b..45aeb2b4c 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -5562,7 +5562,7 @@ void * srt::CUDT::tsbpd(void* param) log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - wakeup_on_signal = tsbpd_cc.wait_until(tsNextDelivery); + bWakeupOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5585,7 +5585,7 @@ void * srt::CUDT::tsbpd(void* param) THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (wakeup_on_signal ? "signal" : "timeout") << "]!!! - " + HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP [" << (bWakeupOnSignal? "signal" : "timeout") << "]!!! - " << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); @@ -8048,14 +8048,13 @@ bool srt::CUDT::getFirstNoncontSequence(int32_t& w_seq, string& w_log_reason) LOGP(cnlog.Error, "IPE: ack can't be sent, buffer doesn't exist and no group membership"); return false; } - { - ScopedLock buflock (m_RcvBufferLock); - bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); - if (has_followers) - w_log_reason = "first lost"; - else - w_log_reason = "expected next"; - } + + ScopedLock buflock (m_RcvBufferLock); + bool has_followers = m_pRcvBuffer->getContiguousEnd((w_seq)); + if (has_followers) + w_log_reason = "first lost"; + else + w_log_reason = "expected next"; return true; } @@ -9090,34 +9089,27 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); } } - // When the drop request was received, it means that there are - // packets for which there will never be ACK sent; if the TSBPD thread - // is currently in the ACK-waiting state, it may never exit. - if (m_bTsbPd) - { - // XXX Likely this is not necessary because: - // 1. In the recv-waiting state, that is, when TSBPD thread - // sleeps forever, it will be woken up anyway on packet - // reception. - // 2. If there are any packets in the buffer and the initial - // packet cell is empty (in which situation any drop could - // occur), TSBPD thread is sleeping timely, until the playtime - // of the first "drop up to" packet. Dropping changes nothing here. - // 3. If the buffer is empty, there's nothing "to drop up to", so - // this function will not change anything in the buffer and so - // in the reception state as well. - // 4. If the TSBPD thread is waiting until a play-ready packet is - // retrieved by the API call (in which case it is also sleeping - // forever until it's woken up by the API call), it may remain - // stalled forever, if the application isn't reading the play-ready - // packet. But this means that the application got stalled anyway. - // TSBPD when woken up could at best state that there's still a - // play-ready packet that is still not retrieved and fall back - // to sleep (forever). - - //HLOGP(inlog.Debug, "DROPREQ: signal TSBPD"); - //rcvtscc.notify_one(); - } + + // NOTE: + // PREVIOUSLY done: notify on rcvtscc if m_bTsbPd + // OLD COMMENT: + // // When the drop request was received, it means that there are + // // packets for which there will never be ACK sent; if the TSBPD thread + // // is currently in the ACK-waiting state, it may never exit. + // + // Likely this is no longer necessary because: + // + // 1. If there's a play-ready packet, either in cell 0 or + // after a drop, TSBPD is sleeping timely, up to the play-time + // of the next ready packet (and the drop region concerned here + // is still a gap to be skipped for this). + // 2. TSBPD sleeps forever when the buffer is empty, in which case + // it will be always woken up on packet reception (see m_bWakeOnRecv). + // And dropping won't happen in that case anyway. Note that the drop + // request will not drop packets that are already received. + // 3. TSBPD sleeps forever when the API call didn't extract the + // data that are ready to play. This isn't a problem if nothing + // except the API call would wake it up. } dropFromLossLists(dropdata[0], dropdata[1]); @@ -10679,21 +10671,6 @@ int srt::CUDT::processData(CUnit* in_unit) HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (SRT): " << Printable(srt_loss_seqs)); sendLossReport(srt_loss_seqs); } - - // This should not be required with the new receiver buffer because - // signalling happens on every packet reception, if it has changed the - // earliest packet position. - /* - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } - else - { - HLOGC(qrlog.Debug, log << CONID() << "loss: socket is not TSBPD, not signaling"); - } - */ } // Separately report loss records of those reported by a filter. From 6ebf94f0e4b19874353c0d412ff744f849b86a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 5 Mar 2024 09:39:38 +0100 Subject: [PATCH 121/517] Withdrawn changes from #2858 as they break ABI compat --- srtcore/api.cpp | 10 ++++---- srtcore/logging.cpp | 4 +++- srtcore/logging.h | 57 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index f9ac6a8d1..e77469de2 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4678,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4687,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); - srt_logger_config.updateLoggersState(); +// srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d309b1b8a..c1625916d 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,6 +23,8 @@ using namespace std; namespace srt_logging { +/* Blocked temporarily until 1.6.0. This causes ABI incompat with 1.5.0 line. + // Note: subscribe() and unsubscribe() functions are being called // in the global constructor and destructor only, as the // Logger objects (and inside them also their LogDispatcher) @@ -85,7 +87,7 @@ void LogDispatcher::SendLogLine(const char* file, int line, const std::string& a } src_config->unlock(); } - +*/ #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index c17781c24..88a2bbfa3 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,7 @@ written by #include #include #include -#include +//#include #include #include #ifdef _WIN32 @@ -116,7 +116,7 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - std::vector loggers; + // std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,10 +139,11 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } - +/* void subscribe(LogDispatcher*); void unsubscribe(LogDispatcher*); void updateLoggersState(); + */ }; // The LogDispatcher class represents the object that is responsible for @@ -154,7 +155,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - srt::sync::atomic enabled; +// srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -165,7 +166,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), - enabled(false), +// enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -193,18 +194,17 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } - config.subscribe(this); - Update(); +// config.subscribe(this); +// Update(); } ~LogDispatcher() { - src_config->unsubscribe(this); +// src_config->unsubscribe(this); } - void Update(); - - bool CheckEnabled() { return enabled; } + // void Update(); + bool CheckEnabled(); // { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,6 +428,22 @@ class Logger } }; +inline bool LogDispatcher::CheckEnabled() +{ + // Don't use enabler caching. Check enabled state every time. + + // These assume to be atomically read, so the lock is not needed + // (note that writing to this field is still mutex-protected). + // It's also no problem if the level was changed at the moment + // when the enabler check is tested here. Worst case, the log + // will be printed just a moment after it was turned off. + const LogConfig* config = src_config; // to enforce using const operator[] + int configured_enabled_fa = config->enabled_fa[fa]; + int configured_maxlevel = config->max_level; + + return configured_enabled_fa && level <= configured_maxlevel; +} + #if HAVE_CXX11 @@ -478,7 +494,24 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); +} + } #endif // INC_SRT_LOGGING_H - From 3601861c13ff8da2d1b7e4d7ec9a5e04978a9f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 5 Mar 2024 10:12:33 +0100 Subject: [PATCH 122/517] [core] Restored logger perf improvements that were blocked up to 1.6.0 --- srtcore/api.cpp | 10 ++++---- srtcore/logging.cpp | 4 +--- srtcore/logging.h | 56 +++++++++------------------------------------ 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index e77469de2..f9ac6a8d1 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -4656,21 +4656,21 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4678,7 +4678,7 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4687,7 +4687,7 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); -// srt_logger_config.updateLoggersState(); + srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index c1625916d..d309b1b8a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -23,8 +23,6 @@ using namespace std; namespace srt_logging { -/* Blocked temporarily until 1.6.0. This causes ABI incompat with 1.5.0 line. - // Note: subscribe() and unsubscribe() functions are being called // in the global constructor and destructor only, as the // Logger objects (and inside them also their LogDispatcher) @@ -87,7 +85,7 @@ void LogDispatcher::SendLogLine(const char* file, int line, const std::string& a } src_config->unlock(); } -*/ + #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index 88a2bbfa3..2d599bb49 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,7 @@ written by #include #include #include -//#include +#include #include #include #ifdef _WIN32 @@ -116,7 +116,7 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - // std::vector loggers; + std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,11 +139,10 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } -/* + void subscribe(LogDispatcher*); void unsubscribe(LogDispatcher*); void updateLoggersState(); - */ }; // The LogDispatcher class represents the object that is responsible for @@ -155,7 +154,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; -// srt::sync::atomic enabled; + srt::sync::atomic enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -166,7 +165,7 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), -// enabled(false), + enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -194,17 +193,18 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } -// config.subscribe(this); -// Update(); + config.subscribe(this); + Update(); } ~LogDispatcher() { -// src_config->unsubscribe(this); + src_config->unsubscribe(this); } - // void Update(); - bool CheckEnabled(); // { return enabled; } + void Update(); + + bool CheckEnabled() { return enabled; } void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -428,22 +428,6 @@ class Logger } }; -inline bool LogDispatcher::CheckEnabled() -{ - // Don't use enabler caching. Check enabled state every time. - - // These assume to be atomically read, so the lock is not needed - // (note that writing to this field is still mutex-protected). - // It's also no problem if the level was changed at the moment - // when the enabler check is tested here. Worst case, the log - // will be printed just a moment after it was turned off. - const LogConfig* config = src_config; // to enforce using const operator[] - int configured_enabled_fa = config->enabled_fa[fa]; - int configured_maxlevel = config->max_level; - - return configured_enabled_fa && level <= configured_maxlevel; -} - #if HAVE_CXX11 @@ -494,24 +478,6 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - (*src_config->log_stream).flush(); - } - src_config->unlock(); -} - } #endif // INC_SRT_LOGGING_H From be1e774003ed9a93bdf676b4936a8cebb28da997 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 24 May 2024 19:04:29 +0200 Subject: [PATCH 123/517] [core] New implementation for the logging/formatting system --- srtcore/buffer_snd.cpp | 2 +- srtcore/common.cpp | 15 +- srtcore/core.cpp | 77 +++-- srtcore/logging.cpp | 4 +- srtcore/logging.h | 21 +- srtcore/sfmt.h | 638 +++++++++++++++++++++++++++++++++++++++ srtcore/socketconfig.cpp | 7 +- 7 files changed, 706 insertions(+), 58 deletions(-) create mode 100644 srtcore/sfmt.h diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index ef1bc498c..aaedd702a 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -393,7 +393,7 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) { // Prevent accessing the last "marker" block LOGC(bslog.Error, - log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount); + log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount.load()); return SRT_MSGNO_CONTROL; } diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 6d747ecaa..843420354 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -277,15 +277,12 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc } else { - LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << std::hex - << peeraddr16[0] << ":" - << peeraddr16[1] << ":" - << peeraddr16[2] << ":" - << peeraddr16[3] << ":" - << peeraddr16[4] << ":" - << peeraddr16[5] << ":" - << peeraddr16[6] << ":" - << peeraddr16[7] << std::dec); + fmt::obufstream peeraddr_form; + peeraddr_form << fmt::sfmt(peeraddr16[0], "04x"); + for (int i = 1; i < 8; ++i) + peeraddr_form << ":" << fmt::sfmt(peeraddr16[i], "04x"); + + LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form); *target_ipv4_addr = 0; if (peer.family() != AF_INET) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 1612830e7..ee76f2bcb 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -88,6 +88,7 @@ using namespace std; using namespace srt; using namespace srt::sync; using namespace srt_logging; +using namespace fmt; const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; @@ -424,7 +425,7 @@ void srt::CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) const int status = m_config.set(optName, optval, optlen); if (status == -1) { - LOGC(aclog.Error, log << CONID() << "OPTION: #" << optName << " UNKNOWN"); + LOGC(aclog.Error, log << CONID() << "OPTION: #" << int(optName) << " UNKNOWN"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -1035,7 +1036,7 @@ size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgt if (srtlen < SRT_HS_E_SIZE) { LOGC(cnlog.Fatal, - log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS_E_SIZE << ")"); + log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << int(SRT_HS_E_SIZE) << ")"); return 0; } @@ -1771,10 +1772,14 @@ bool srt::CUDT::createSrtHandshake( if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP)) { m_RejectReason = SRT_REJ_IPE; + static const char onoff[2] = {'-', '+'}; LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); + << " Socket state: " + << onoff[m_bConnected] << "connected, " + << onoff[m_bConnecting] << "connecting, " + << onoff[m_bBroken] << "broken, " + << onoff[m_bClosing] << "closing."); return false; } @@ -2102,8 +2107,9 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint } 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])); + << " vers=0x" << sfmt(srtdata[SRT_HS_VERSION], "x") + << " opts=0x" << sfmt(srtdata[SRT_HS_FLAGS], "x") + << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -2522,7 +2528,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS-ext HSREQ found but invalid size: " << bytelen - << " (expected: " << SRT_HS_E_SIZE << ")"); + << " (expected: " << int(SRT_HS_E_SIZE) << ")"); return false; // don't interpret } @@ -2548,7 +2554,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS-ext HSRSP found but invalid size: " << bytelen - << " (expected: " << SRT_HS_E_SIZE << ")"); + << " (expected: " << int(SRT_HS_E_SIZE) << ")"); return false; // don't interpret } @@ -3451,7 +3457,7 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { HLOGC(gmlog.Debug, log << CONID() << "synchronizeWithGroup: DERIVED ISN: RCV=%" << m_iRcvLastAck << " -> %" << rcv_isn - << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck + << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck.load() << " -> %" << snd_isn << " (shift by " << CSeqNo::seqcmp(snd_isn, m_iSndLastAck) << ")"); setInitialRcvSeq(rcv_isn); setInitialSndSeq(snd_isn); @@ -3460,7 +3466,7 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { HLOGC(gmlog.Debug, log << CONID() << "synchronizeWithGroup: DEFINED ISN: RCV=%" << m_iRcvLastAck << " SND=%" - << m_iSndLastAck); + << m_iSndLastAck.load()); } } #endif @@ -4098,11 +4104,16 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas // m_pCryptoControl can be NULL if the socket has been closed already. See issue #2231. if (!m_pCryptoControl) { + static const char onoff[2] = {'-', '+'}; m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Error, log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", opened " << m_bOpened << ", closing=" << m_bClosing << "."); + << " Socket state: " + << onoff[m_bConnected] << "connected, " + << onoff[m_bConnecting] << "connecting, " + << onoff[m_bBroken] << "broken, " + << onoff[m_bOpened] << "opened, " + << onoff[m_bClosing] << "closing."); return CONN_REJECT; } // This is a periodic handshake update, so you need to extract the KM data from the @@ -5466,8 +5477,8 @@ void * srt::CUDT::tsbpd(void* param) if (self->frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) { LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno - << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') - << (timediff_us % 1000) << " ms " << why); + << " delayed for " << (timediff_us / 1000) << "." + << sfmt(timediff_us % 1000, "03") << " ms " << why); } #if SRT_ENABLE_FREQUENT_LOG_TRACE else @@ -6573,7 +6584,7 @@ int srt::CUDT::sndDropTooLate() } HLOGC(qslog.Debug, - log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" << dpkts << "pkt " << dbytes + log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo.load() << ") n=" << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); #if ENABLE_BONDING @@ -8408,7 +8419,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // included, but it also triggers for any other kind of invalid value. // This check MUST BE DONE before making any operation on this number. LOGC(inlog.Error, log << CONID() << "ACK: IPE/EPE: received invalid ACK value: " << ackdata_seqno - << " " << std::hex << ackdata_seqno << " (IGNORED)"); + << " " << sfmt(ackdata_seqno, "x") << " (IGNORED)"); return; } @@ -8465,7 +8476,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // this should not happen: attack or bug LOGC(gglog.Error, log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current " - << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); + << m_iSndCurrSeqNo.load() << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); m_bBroken = true; m_iBrokenCounter = 0; return; @@ -8667,14 +8678,14 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr 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 << ")"); + << ", RTT (EWMA): " << m_iSRTT.load() << ")"); return; } LOGC(inlog.Error, log << CONID() << "ACK record not found, can't estimate RTT " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iSRTT << ")"); + << ", RTT (EWMA): " << m_iSRTT.load() << ")"); return; } @@ -8777,7 +8788,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // LO must not be greater than HI. // HI must not be greater than the most recent sent seq. LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT rng " << losslist_lo << " - " << losslist_hi - << " with last sent " << m_iSndCurrSeqNo << " - DISCARDING"); + << " with last sent " << m_iSndCurrSeqNo.load() << " - DISCARDING"); secure = false; wrong_loss = losslist_hi; break; @@ -8846,7 +8857,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) { LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT pkt %" << losslist[i] - << " with last sent %" << m_iSndCurrSeqNo << " - DISCARDING"); + << " with last sent %" << m_iSndCurrSeqNo.load() << " - DISCARDING"); // loss_seq must not be greater than the most recent sent seq secure = false; wrong_loss = losslist[i]; @@ -8882,7 +8893,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (!secure) { LOGC(inlog.Warn, - log << CONID() << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo + log << CONID() << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo.load() << " vs loss %" << wrong_loss); // this should not happen: attack or bug m_bBroken = true; @@ -9074,7 +9085,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) else { HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); + << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo.load()); } } @@ -9322,7 +9333,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // was completely ignored. LOGC(qrlog.Error, log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " - << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck << ")=" << offset + << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck.load() << ")=" << offset << ". Continue, request DROP"); // No matter whether this is right or not (maybe the attack case should be @@ -9747,7 +9758,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_iCongestionWindow - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo.load() << ")=" << flightspan); return false; } @@ -9763,7 +9774,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { // Some packets were skipped due to TTL expiry. m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); - HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo + HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo.load() << " due to TTL expiry"); } @@ -9967,7 +9978,7 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) if (diff < 0 || diff > CSeqNo::m_iSeqNoTH) { LOGC(gslog.Error, log << CONID() << "IPE: Overriding with seq %" << seq << " DISCREPANCY against current %" - << m_iSndCurrSeqNo << " and next sched %" << m_iSndNextSeqNo << " - diff=" << diff); + << m_iSndCurrSeqNo.load() << " and next sched %" << m_iSndNextSeqNo.load() << " - diff=" << diff); return false; } @@ -9984,7 +9995,7 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) // not yet sent. HLOGC(gslog.Debug, - log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo + log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo.load() << " send-seq=" << m_iSndCurrSeqNo.load() << " (unchanged)"); return true; } @@ -10107,7 +10118,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& "SEQUENCE DISCREPANCY. BREAKING CONNECTION." " %" << rpkt.seqno() << " buffer=(%" << bufseq - << ":%" << m_iRcvCurrSeqNo // -1 = size to last index + << ":%" << m_iRcvCurrSeqNo.load() // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) << "), " << (m_pRcvBuffer->capacity() - bufidx + 1) << " past max. Reception no longer possible. REQUESTING TO CLOSE."); @@ -10227,7 +10238,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bufinfo << " BUF.s=" << m_pRcvBuffer->capacity() << " avail=" << (int(m_pRcvBuffer->capacity()) - ackidx) << " buffer=(%" << bufseq - << ":%" << m_iRcvCurrSeqNo // -1 = size to last index + << ":%" << m_iRcvCurrSeqNo.load() // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) << ")"; } @@ -10725,7 +10736,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) { // Reject the change because that would shift the reception pointer backwards. HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq in @" << m_SocketID - << ": backward setting rejected: %" << m_iRcvCurrSeqNo + << ": backward setting rejected: %" << m_iRcvCurrSeqNo.load() << " -> %" << new_last_rcv); return; } @@ -11220,7 +11231,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (result == -1) { hs.m_iReqType = URQFailure(error); - LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << hs.m_iReqType << " - " << srt_rejectreason_str(error)); + LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << int(hs.m_iReqType) << " - " << srt_rejectreason_str(error)); } // The `acpu` not NULL means connection exists, the `result` should be 0. It is not checked here though. @@ -11324,7 +11335,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) } } } - LOGC(cnlog.Debug, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + LOGC(cnlog.Debug, log << CONID() << "listen ret: " << int(hs.m_iReqType) << " - " << RequestTypeStr(hs.m_iReqType)); return RejectReasonForURQ(hs.m_iReqType); } diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d0ba3fd4a..8ccfa30f1 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -43,7 +43,7 @@ LogDispatcher::Proxy LogDispatcher::operator()() return Proxy(*this); } -void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) +void LogDispatcher::CreateLogLinePrefix(fmt::obufstream& serr) { using namespace std; using namespace srt; @@ -60,7 +60,7 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) { - serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + serr << tmp_buf << fmt::sfmt(tv.tv_usec, "06"); } } diff --git a/srtcore/logging.h b/srtcore/logging.h index 2ec5f46aa..0917af9ac 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,6 @@ written by #include #include #include -#include #include #ifdef _WIN32 #include "win/wintime.h" @@ -29,6 +28,8 @@ written by #include #endif +#include "sfmt.h" + #include "srt.h" #include "utilities.h" #include "threadname.h" @@ -192,7 +193,7 @@ struct SRT_API LogDispatcher bool CheckEnabled(); - void CreateLogLinePrefix(std::ostringstream&); + void CreateLogLinePrefix(fmt::obufstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); // log.Debug("This is the ", nth, " time"); <--- C++11 only. @@ -285,7 +286,7 @@ struct LogDispatcher::Proxy { LogDispatcher& that; - std::ostringstream os; + fmt::obufstream os; // Cache the 'enabled' state in the beginning. If the logging // becomes enabled or disabled in the middle of the log, we don't @@ -341,7 +342,7 @@ struct LogDispatcher::Proxy if ( that_enabled ) { if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 ) - os << std::endl; + os << fmt::seol; that.SendLogLine(i_file, i_line, area, os.str()); } // Needed in destructor? @@ -435,10 +436,10 @@ inline bool LogDispatcher::CheckEnabled() //extern std::mutex Debug_mutex; -inline void PrintArgs(std::ostream&) {} +inline void PrintArgs(fmt::obufstream&) {} template -inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) +inline void PrintArgs(fmt::obufstream& serr, Arg1&& arg1, Args&&... args) { serr << std::forward(arg1); PrintArgs(serr, args...); @@ -448,12 +449,12 @@ template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - std::ostringstream serr; + fmt::obufstream serr; CreateLogLinePrefix(serr); PrintArgs(serr, args...); if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << std::endl; + serr << fmt::seol; // Not sure, but it wasn't ever used. SendLogLine(file, line, area, serr.str()); @@ -466,12 +467,12 @@ template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - std::ostringstream serr; + fmt::obufstream serr; CreateLogLinePrefix(serr); serr << arg; if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << std::endl; + serr << fmt::seol; // Not sure, but it wasn't ever used. SendLogLine(file, line, area, serr.str()); diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h new file mode 100644 index 000000000..8eda16462 --- /dev/null +++ b/srtcore/sfmt.h @@ -0,0 +1,638 @@ +// Formatting library for C++ - C++03 compat version of on-demand tagged format API. +// +// Copyright (c) 2024 - present, Mikołaj Małecki +// All rights reserved. +// +// For the license information refer to format.h. + +// This is a header-only lightweight C++03-compatible formatting library, +// which provides the on-demand tagged format API and iostream-style wrapper +// for FILE type from stdio. It has nothing to do with the rest of the {fmt} +// library, except that it reuses the namespace. + +#include +#include +#include +#include +#include + +namespace fmt +{ + +namespace internal +{ + +template +class form_memory_buffer +{ +public: + static const size_t INITIAL_SIZE = PARAM_INITIAL_SIZE; + typedef std::list< std::vector > slices_t; + +private: + char first[INITIAL_SIZE]; + slices_t slices; + + size_t initial; // size used in `first` + size_t reserved; // total size plus slices in reserve + size_t total; // total size in use + +public: + form_memory_buffer(): initial(0), reserved(0), total(0) {} + + // For constants + template + form_memory_buffer(const char (&array)[N]): initial(N-1), reserved(N-1), total(N-1) + { + memcpy(first, array, N); + } + + size_t avail() + { + return reserved - total; + } + + char* get_first() { return first; } + const char* get_first() const { return first; } + size_t first_size() const { return initial; } + const slices_t& get_slices() const { return slices; } + size_t size() const { return total; } + bool empty() const { return total == 0; } + + void append(char c) + { + char wrap[1] = {c}; + append(wrap, 1); + } + + // NOTE: append ignores the reservation. It writes + // where the currently available space is. Use expose() + // and commit() together or not at all. + void append(const char* val, size_t size) + { + if (size == 0) + return; + + if (slices.empty()) + { + if (size < INITIAL_SIZE - initial) + { + // Still free space in first. + memcpy(first + initial, val, size); + initial += size; + total = initial; + if (reserved < total) + reserved = total; + return; + } + } + + slices.push_back(std::vector(val, val+size)); + total += size; + if (reserved < total) + reserved = total; + } + + char* expose(size_t size) + { + // Repeated exposure simply extends the + // reservation, if required more, or is ignored, + // if required less. + + // Note that ort + + size_t already_reserved = reserved - total; + if (already_reserved >= size) + { + // Identify the reserved region + if (slices.empty()) + { + reserved = total + size; + return first + initial; + } + + std::vector& last = slices.back(); + // Exceptionally resize that part if it doesn't + // fit. + if (last.size() != size) + last.resize(size); + reserved = total + size; + return &last[0]; + } + + // Check if you have any size available + // beyond the current reserved space. + // If not, allocate. + if (slices.empty()) + { + // Not yet resolved to use of the slices, + // so check free space in first. The value of + // 'reserved' should be still < INITIAL_SIZE. + if (INITIAL_SIZE - total >= size) + { + char* b = first + total; + reserved = total + size; + return b; + } + } + + // Otherwise allocate a new slice + // Check first if the last slice was already reserved + std::vector* plast = &slices.back(); + if (!already_reserved) + { + slices.push_back( std::vector() ); + plast = &slices.back(); + } + + plast->reserve(size); + plast->resize(size); + reserved = total + size; + return &(*plast)[0]; + } + + // Remove the last 'size' chars from reservation + bool unreserve(size_t size) + { + if (size > reserved - total) + return false; + + if (!slices.empty()) + { + // Check the last slice if it contains that size + std::vector& last = slices.back(); + if (last.size() < size) + return false; + + size_t remain = last.size() - size; + if (!remain) + { + slices.pop_back(); + reserved -= size; + return true; + } + + last.erase(last.begin() + remain, last.end()); + } + // Otherwise the space is in the initial buffer. + + reserved -= size; + return true; + } + + void commit() + { + total = reserved; + if (slices.empty()) + { + // This means we don't use extra slices, so + // this size must be also repeated in initial + initial = reserved; + } + } +}; + +template +struct CheckChar +{ + static bool is(char c, const char (&series)[N]) + { + return c == series[I] || CheckChar::is(c, series); + } +}; + +template +struct CheckChar +{ + // Terminal version - if none interrupted with true, + // eventually return false. + static bool is(char , const char (&)[N]) { return false; } +}; + +template inline +bool isanyof(char c, const char (&series)[N]) +{ + return CheckChar::is(c, series); +} + +template inline +bool isnum_or(char c, const char (&series)[N]) +{ + if (c >= '0' && c <= '9') + return true; + return isanyof(c, series); +} + +template +struct Ensure +{ +}; + +template +struct Ensure +{ + typename AnyType::wrong_condition v = AnyType::wrong_condition; +}; + +template inline +form_memory_buffer<> fix_format(const char* fmt, + const char (&allowed)[N1], + const char (&typed)[N2], + const char (&deftype)[N3], + const char* warn) +{ + // All these arrays must contain at least 2 elements, + // that is one character and terminating zero. + //Ensure= 2> c1; + Ensure= 2> c2; (void)c2; + Ensure= 2> c3; (void)c3; + + form_memory_buffer<> buf; + buf.append('%'); + + bool warn_error = false; + if (fmt) + { + size_t len = strlen(fmt); + for (size_t i = 0; i < len; ++i) + { + char c = fmt[i]; + if (internal::isnum_or(c, allowed)) + { + buf.append(c); + continue; + } + + if (internal::isanyof(c, typed)) + { + // If you have found any numbase character, + // add first all characters from the default, + // EXCEPT the last one. + buf.append(deftype, N3-2); + + // that's it, and we're done here. + buf.append(c); + return buf; + } + + // If any other character is found, add the warning + warn_error = true; + break; + } + } + + buf.append(deftype, N3); + + if (warn_error && warn) + { + buf.append(warn, strlen(warn)); + } + return buf; +} + + +template +struct Formatter +{ +}; + +#define SFMT_FORMAT_FIXER_TPL(tplarg, type, allowed, typed, deftype, warn)\ +template \ +struct Formatter \ +{ \ + static form_memory_buffer<> fix(const char* fmt) \ + { \ + return internal::fix_format(fmt, allowed, typed, deftype, warn); \ + } \ +} +#define SFMT_FORMAT_FIXER(type, allowed, typed, deftype, warn) \ + SFMT_FORMAT_FIXER_TPL(, type, allowed, typed, deftype, warn) + + +// So, format in the format spec is: +// +// (missing): add the default format +// Using: diouxX - specify the numeric base +// Using efg - specify the float style + +// Modifiers like "h", "l", or "L" shall not +// be used. They will be inserted if needed. + +SFMT_FORMAT_FIXER(int, "+- '#", "dioxX", "i", ""); +// Short is simple because it's aligned to int anyway +SFMT_FORMAT_FIXER(short int, "+- '#", "dioxX", "hi", ""); + +SFMT_FORMAT_FIXER(long int, "+- '#", "dioxX", "li", ""); + +SFMT_FORMAT_FIXER(long long int, "+- '#", "dioxX", "lli", ""); + +SFMT_FORMAT_FIXER(unsigned int, "+- '#", "uoxX", "u", ""); + +SFMT_FORMAT_FIXER(unsigned short int, "+- '#", "uoxX", "hu", ""); + +SFMT_FORMAT_FIXER(unsigned long int, "+- '#", "uoxX", "lu", ""); + +SFMT_FORMAT_FIXER(unsigned long long int, "+- '#", "uoxX", "llu", ""); + +SFMT_FORMAT_FIXER(double, "+- '#.", "EeFfgGaA", "g", ""); +SFMT_FORMAT_FIXER(float, "+- '#.", "EeFfgGaA", "g", ""); +SFMT_FORMAT_FIXER(long double, "+- '#.", "EeFfgGaA", "Lg", ""); + +SFMT_FORMAT_FIXER(char, "", "c", "c", ""); +SFMT_FORMAT_FIXER(std::string, "", "s", "s", ""); +SFMT_FORMAT_FIXER(const char*, "", "s", "s", ""); +SFMT_FORMAT_FIXER(char*, "", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, const char[N], "", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, char[N], "", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(class Type, Type*, "", "p", "p", ""); + +#undef SFMT_FORMAT_FIXER_TPL +#undef SFMT_FORMAT_FIXER + +template inline +void write_default(Stream& str, const Value& val); + +} + +class ostdiostream +{ +protected: + mutable FILE* in; + +public: + + FILE* raw() const { return in; } + + ostdiostream(FILE* f): in(f) {} + + ostdiostream& operator<<(const char* t) + { + std::fputs(t, in); + return *this; + } + + ostdiostream& operator<<(const std::string& s) + { + std::fputs(s.c_str(), in); + return *this; + } + + template + ostdiostream& operator<<(const internal::form_memory_buffer& b) + { + using namespace internal; + // Copy all pieces one by one + if (b.size() == 0) + return *this; + + std::fwrite(b.get_first(), 1, b.first_size(), in); + for (form_memory_buffer<>::slices_t::const_iterator i = b.get_slices().begin(); + i != b.get_slices().end(); ++i) + { + const char* data = &(*i)[0]; + std::fwrite(data, 1, i->size(), in); + } + return *this; + } + + template + ostdiostream& operator<<(const Value& v) + { + internal::write_default(*this, v); + return *this; + } +}; + + +class ofilestream: public ostdiostream +{ +public: + + ofilestream(): ostdiostream(0) {} + + ofilestream(const std::string& name, const std::string& mode = "") + : ostdiostream(0) // Set NULL initially, but then override + { + open(name, mode); + } + + bool good() const { return in; } + + void open(const std::string& name, const std::string& mode = "") + { + if (mode == "") + in = std::fopen(name.c_str(), "w"); + else + in = std::fopen(name.c_str(), mode.c_str()); + } + + // For the use of other functions than fopen() that can create the stream, + // but they still create FILE* that should be closed using fclose(). + void attach(FILE* other) + { + in = other; + } + + FILE* detach() + { + FILE* sav = in; + in = 0; + return sav; + } + + int close() + { + int retval = 0; + if (in) + { + retval = std::fclose(in); + in = 0; + } + return retval; + } + + ~ofilestream() + { + if (in) + std::fclose(in); + } +}; + +class obufstream +{ +protected: + internal::form_memory_buffer<> buffer; + +public: + + obufstream() {} + + obufstream& operator<<(const char* t) + { + size_t len = strlen(t); + buffer.append(t, len); + return *this; + } + + obufstream& operator<<(const std::string& s) + { + buffer.append(s.data(), s.size()); + return *this; + } + + template + obufstream& operator<<(const internal::form_memory_buffer& b) + { + using namespace internal; + // Copy all pieces one by one + if (b.size() == 0) + return *this; + + buffer.append(b.get_first(), b.first_size()); + for (form_memory_buffer<>::slices_t::const_iterator i = b.get_slices().begin(); + i != b.get_slices().end(); ++i) + { + // Would be tempting to move the blocks, but C++03 doesn't feature moving. + const char* data = &(*i)[0]; + buffer.append(data, i->size()); + } + return *this; + } + + obufstream& operator<<(const obufstream& source) + { + return *this << source.buffer; + } + + template + obufstream& operator<<(const Value& v) + { + internal::write_default(*this, v); + return *this; + } + + std::string str() const + { + using namespace internal; + std::string out; + if (buffer.empty()) + return out; + + out.reserve(buffer.size() + 1); + out.append(buffer.get_first(), buffer.first_size()); + for (form_memory_buffer<>::slices_t::const_iterator i = buffer.get_slices().begin(); + i != buffer.get_slices().end(); ++i) + { + // Would be tempting to move the blocks, but C++03 doesn't feature moving. + const char* data = &(*i)[0]; + out.append(data, i->size()); + } + return out; + } + + size_t size() const { return buffer.size(); } + + template + void copy_to(OutputContainer& out) const + { + using namespace internal; + + std::copy(buffer.get_first(), buffer.get_first() + buffer.first_size(), + std::back_inserter(out)); + + for (form_memory_buffer<>::slices_t::const_iterator i = buffer.get_slices().begin(); + i != buffer.get_slices().end(); ++i) + { + // Would be tempting to move the blocks, but C++03 doesn't feature moving. + const char* data = &(*i)[0]; + std::copy(data, data + i->size(), std::back_inserter(out)); + } + } +}; + +template inline +internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) +{ + using namespace internal; + + form_memory_buffer<> fstr = Formatter::fix(fmtspec); + form_memory_buffer<> out; + size_t bufsize = form_memory_buffer<>::INITIAL_SIZE; + + // Reserve the maximum initial first, then shrink. + char* buf = out.expose(bufsize); + + // We want to use this buffer as a NUL-terminated string. + // So we need to add NUL character oursevles, form_memory_buffer<> + // doesn't do it an doesn't use the NUL-termination. + fstr.append('\0'); + + size_t valsize = snprintf(buf, bufsize, fstr.get_first(), val); + + // Deemed impossible to happen, but still + if (valsize == bufsize) + { + bufsize *= 2; + // Just try again with one extra size, if this won't + // suffice, just add <...> at the end. + buf = out.expose(bufsize); + valsize = snprintf(buf, bufsize, fstr.get_first(), val); + if (valsize == bufsize) + { + char* end = buf + bufsize - 6; + strcpy(end, "<...>"); + } + } + + size_t unused = bufsize - valsize; + out.unreserve(unused); + out.commit(); + return out; +} + +template inline +std::string sfmts(const Value& val, const char* fmtspec = 0) +{ + using namespace internal; + + std::string out; + form_memory_buffer<> b = sfmt(val, fmtspec); + if (b.size() == 0) + return out; + + out.reserve(b.size()); + + out.append(b.get_first(), b.first_size()); + for (form_memory_buffer<>::slices_t::const_iterator i = b.get_slices().begin(); + i != b.get_slices().end(); ++i) + { + const char* data = &(*i)[0]; + out.append(data, i->size()); + } + + return out; +} + +namespace internal +{ +template inline +void write_default(Stream& s, const Value& v) +{ + s << sfmt(v, ""); +} +} + +// Semi-manipulator to add the end-of-line. +const internal::form_memory_buffer<2> seol ("\n"); + +// Another manipulator. You can add yourself others the same way. +const struct os_flush_manip {} sflush; + +inline ostdiostream& operator<<(ostdiostream& sout, const os_flush_manip&) +{ + std::fflush(sout.raw()); + return sout; +}; + + +} diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index d44330f78..9c5a9513e 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -744,8 +744,8 @@ struct CSrtConfigSetter { co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" - << std::hex << co.uKmPreAnnouncePkt); + log << "SRTO_KMREFRESHRATE=0x" << fmt::sfmt(km_refresh, "x") << ": setting SRTO_KMPREANNOUNCE=0x" + << fmt::sfmt(co.uKmPreAnnouncePkt, "x")); } } }; @@ -770,7 +770,8 @@ struct CSrtConfigSetter if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + log << "SRTO_KMPREANNOUNCE=0x" << fmt::sfmt(km_preanno, "x") + << " exceeds KmRefresh/2, 0x" << fmt::sfmt((kmref - 1) / 2) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } From 308af582cbb21ca38ac83c9bf6c04000a03f32bf Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 24 May 2024 19:22:07 +0200 Subject: [PATCH 124/517] Fixed parts in non-default-enabled code parts --- srtcore/buffer_snd.cpp | 2 +- srtcore/core.cpp | 6 +++--- srtcore/group.cpp | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index aaedd702a..998e080ab 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -141,7 +141,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) const int iNumBlocks = countNumPacketsRequired(len, iPktLen); HLOGC(bslog.Debug, - log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize); + log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount.load() << "/" << m_iSize); // Retrieve current time before locking the mutex to be closer to packet submission event. const steady_clock::time_point tnow = steady_clock::now(); diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ee76f2bcb..e321035d4 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -3125,7 +3125,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A { m_RejectReason = SRT_REJ_GROUP; LOGC(cnlog.Error, - log << CONID() << "HS/GROUP: incorrect group type value " << gtp << " (max is " << SRT_GTYPE_E_END << ")"); + log << CONID() << "HS/GROUP: incorrect group type value " << int(gtp) << " (max is " << int(SRT_GTYPE_E_END) << ")"); return false; } @@ -3299,8 +3299,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 if (gp->type() != gtp) { LOGC(gmlog.Error, - log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp - << " agent group=$" << gp->id() << " type" << gp->type()); + log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << int(gtp) + << " agent group=$" << gp->id() << " type" << int(gp->type())); return -1; } diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 0927d085a..5fea652d9 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1023,7 +1023,7 @@ int CUDTGroup::send(const char* buf, int len, SRT_MSGCTRL& w_mc) switch (m_type) { default: - LOGC(gslog.Error, log << "CUDTGroup::send: not implemented for type #" << m_type); + LOGC(gslog.Error, log << "CUDTGroup::send: not implemented for type #" << int(m_type)); throw CUDTException(MJ_SETUP, MN_INVAL, 0); case SRT_GTYPE_BROADCAST: @@ -2189,9 +2189,10 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { if (!m_bOpened || !m_bConnected) { + const char onoff[2] = {'-', '+'}; LOGC(grlog.Error, - log << boolalpha << "grp/recv: $" << id() << ": ABANDONING: opened=" << m_bOpened - << " connected=" << m_bConnected); + log << "grp/recv: $" << id() << ": ABANDONING: opened" << onoff[m_bOpened] + << " connected" << onoff[m_bConnected]); throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); } @@ -2249,7 +2250,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { LOGC(grlog.Error, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" - << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + << m_RcvBaseSeqNo.load() << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); ps->core().m_bBroken = true; broken.insert(ps); continue; From 5ae2a03e22efbf9ad7321ec8967910b695d3825f Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Sat, 25 May 2024 23:51:40 +0200 Subject: [PATCH 125/517] Updated sfmt.h with reimplementation fixes --- srtcore/sfmt.h | 82 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 8eda16462..65632ad9a 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -190,6 +190,14 @@ class form_memory_buffer initial = reserved; } } + + void clear() + { + slices.clear(); + total = 0; + reserved = 0; + initial = 0; + } }; template @@ -291,22 +299,19 @@ form_memory_buffer<> fix_format(const char* fmt, } -template -struct Formatter -{ -}; - -#define SFMT_FORMAT_FIXER_TPL(tplarg, type, allowed, typed, deftype, warn)\ -template \ -struct Formatter \ +#define SFMT_FORMAT_FIXER(TYPE, ALLOWED, TYPED, DEFTYPE, WARN) \ +form_memory_buffer<> apply_format_fix(TYPE, const char* fmt) \ { \ - static form_memory_buffer<> fix(const char* fmt) \ - { \ - return internal::fix_format(fmt, allowed, typed, deftype, warn); \ - } \ + return fix_format(fmt, ALLOWED, TYPED, DEFTYPE, WARN); \ +} + +#define SFMT_FORMAT_FIXER_TPL(TPAR, TYPE, ALLOWED, TYPED, DEFTYPE, WARN) \ +template\ +form_memory_buffer<> apply_format_fix(TYPE, const char* fmt)\ +{\ + return fix_format(fmt, ALLOWED, TYPED, DEFTYPE, WARN); \ } -#define SFMT_FORMAT_FIXER(type, allowed, typed, deftype, warn) \ - SFMT_FORMAT_FIXER_TPL(, type, allowed, typed, deftype, warn) + // So, format in the format spec is: @@ -342,8 +347,8 @@ SFMT_FORMAT_FIXER(char, "", "c", "c", ""); SFMT_FORMAT_FIXER(std::string, "", "s", "s", ""); SFMT_FORMAT_FIXER(const char*, "", "s", "s", ""); SFMT_FORMAT_FIXER(char*, "", "s", "s", ""); -SFMT_FORMAT_FIXER_TPL(size_t N, const char[N], "", "s", "s", ""); -SFMT_FORMAT_FIXER_TPL(size_t N, char[N], "", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, const char (&)[N], "", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, char (&)[N], "", "s", "s", ""); SFMT_FORMAT_FIXER_TPL(class Type, Type*, "", "p", "p", ""); #undef SFMT_FORMAT_FIXER_TPL @@ -467,6 +472,11 @@ class obufstream obufstream() {} + void clear() + { + buffer.clear(); + } + obufstream& operator<<(const char* t) { size_t len = strlen(t); @@ -550,12 +560,27 @@ class obufstream } }; +namespace internal +{ +template +static inline size_t SNPrintfOne(char* buf, size_t bufsize, const char* fmt, const ValueType& val) +{ + return std::snprintf(buf, bufsize, fmt, val); +} + + +static inline size_t SNPrintfOne(char* buf, size_t bufsize, const char* fmt, const std::string& val) +{ + return std::snprintf(buf, bufsize, fmt, val.c_str()); +} +} + template inline internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) { using namespace internal; - form_memory_buffer<> fstr = Formatter::fix(fmtspec); + form_memory_buffer<> fstr = apply_format_fix(val, fmtspec); form_memory_buffer<> out; size_t bufsize = form_memory_buffer<>::INITIAL_SIZE; @@ -567,7 +592,7 @@ internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) // doesn't do it an doesn't use the NUL-termination. fstr.append('\0'); - size_t valsize = snprintf(buf, bufsize, fstr.get_first(), val); + size_t valsize = SNPrintfOne( buf, bufsize, fstr.get_first(), val); // Deemed impossible to happen, but still if (valsize == bufsize) @@ -590,7 +615,17 @@ internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) return out; } -template inline +namespace internal +{ +template inline +void write_default(Stream& s, const Value& v) +{ + s << sfmt(v, ""); +} +} + + +template std::string sfmts(const Value& val, const char* fmtspec = 0) { using namespace internal; @@ -613,15 +648,6 @@ std::string sfmts(const Value& val, const char* fmtspec = 0) return out; } -namespace internal -{ -template inline -void write_default(Stream& s, const Value& v) -{ - s << sfmt(v, ""); -} -} - // Semi-manipulator to add the end-of-line. const internal::form_memory_buffer<2> seol ("\n"); From 089dd51d3706474432a333b734f43b64fb6b384e Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Sun, 26 May 2024 00:35:01 +0200 Subject: [PATCH 126/517] Fixed heavy logging cases and atomics. Withdrawn changes for enums. Updated sfmt implementation --- srtcore/buffer_snd.cpp | 2 +- srtcore/congctl.cpp | 8 ++-- srtcore/core.cpp | 73 +++++++++++++++++++------------------ srtcore/group.cpp | 2 +- srtcore/queue.cpp | 2 +- srtcore/sfmt.h | 8 ++-- testing/testactivemedia.cpp | 3 +- testing/testactivemedia.hpp | 2 +- 8 files changed, 52 insertions(+), 48 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 998e080ab..901e829b6 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -242,7 +242,7 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) const int iNumBlocks = countNumPacketsRequired(len, iPktLen); HLOGC(bslog.Debug, - log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen + log << "addBufferFromFile: size=" << m_iCount.load() << " reserved=" << m_iSize << " needs=" << iPktLen << " buffers for " << len << " bytes"); // dynamically increase sender buffer diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index b9265c046..bd4c9f162 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -87,7 +87,7 @@ class LiveCC: public SrtCongestionControlBase m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) - HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize); + HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize.load()); updatePktSndPeriod(); @@ -154,7 +154,7 @@ class LiveCC: public SrtCongestionControlBase // thread will pick up a "slightly outdated" average value from this // field - this is insignificant. m_zSndAvgPayloadSize = avg_iir<128, size_t>(m_zSndAvgPayloadSize, packet.getLength()); - HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize); + HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize.load()); } /// @brief On RTO event update an inter-packet send interval. @@ -179,7 +179,7 @@ class LiveCC: public SrtCongestionControlBase const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod - << " by avg pktsize=" << m_zSndAvgPayloadSize + << " by avg pktsize=" << m_zSndAvgPayloadSize.load() << ", bw=" << m_llSndMaxBW); } @@ -595,7 +595,7 @@ class FileCC : public SrtCongestionControlBase { m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << setprecision(6) << m_dCWndSize << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); + << fmt::sfmt(m_dCWndSize, "06") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/core.cpp b/srtcore/core.cpp index e321035d4..00ab9367b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -425,7 +425,7 @@ void srt::CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) const int status = m_config.set(optName, optval, optlen); if (status == -1) { - LOGC(aclog.Error, log << CONID() << "OPTION: #" << int(optName) << " UNKNOWN"); + LOGC(aclog.Error, log << CONID() << "OPTION: #" << optName << " UNKNOWN"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -1036,7 +1036,7 @@ size_t srt::CUDT::fillSrtHandshake(uint32_t *aw_srtdata, size_t srtlen, int msgt if (srtlen < SRT_HS_E_SIZE) { LOGC(cnlog.Fatal, - log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << int(SRT_HS_E_SIZE) << ")"); + log << CONID() << "IPE: fillSrtHandshake: buffer too small: " << srtlen << " (expected: " << SRT_HS_E_SIZE << ")"); return 0; } @@ -2343,8 +2343,8 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion) - << " Flags: SND:" << setw(8) << setfill('0') << hex << m_uPeerSrtFlags - << setw(0) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); + << " Flags: SND:" << sfmt(m_uPeerSrtFlags, "08x") + << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); // Basic version check if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) { @@ -2528,7 +2528,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS-ext HSREQ found but invalid size: " << bytelen - << " (expected: " << int(SRT_HS_E_SIZE) << ")"); + << " (expected: " << SRT_HS_E_SIZE << ")"); return false; // don't interpret } @@ -2554,7 +2554,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, m_RejectReason = SRT_REJ_ROGUE; LOGC(cnlog.Error, log << CONID() << "HS-ext HSRSP found but invalid size: " << bytelen - << " (expected: " << int(SRT_HS_E_SIZE) << ")"); + << " (expected: " << SRT_HS_E_SIZE << ")"); return false; // don't interpret } @@ -3125,7 +3125,7 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A { m_RejectReason = SRT_REJ_GROUP; LOGC(cnlog.Error, - log << CONID() << "HS/GROUP: incorrect group type value " << int(gtp) << " (max is " << int(SRT_GTYPE_E_END) << ")"); + log << CONID() << "HS/GROUP: incorrect group type value " << gtp << " (max is " << SRT_GTYPE_E_END << ")"); return false; } @@ -3299,8 +3299,8 @@ SRTSOCKET srt::CUDT::makeMePeerOf(SRTSOCKET peergroup, SRT_GROUP_TYPE gtp, uint3 if (gp->type() != gtp) { LOGC(gmlog.Error, - log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << int(gtp) - << " agent group=$" << gp->id() << " type" << int(gp->type())); + log << CONID() << "HS: GROUP TYPE COLLISION: peer group=$" << peergroup << " type " << gtp + << " agent group=$" << gp->id() << " type" << gp->type()); return -1; } @@ -3883,7 +3883,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << CONID() << "startConnect: END. Parameters: mss=" << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize() - << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); + << " rtt=" << m_iSRTT.load() << " bw=" << m_iBandwidth.load()); } // Asynchronous connection @@ -4022,7 +4022,7 @@ void srt::CUDT::sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& r_rsppkt.setLength(size); HLOGC(cnlog.Debug, log << CONID() << "sendRendezvousRejection: using code=" << m_ConnReq.m_iReqType - << " for reject reason code " << m_RejectReason << " (" << srt_rejectreason_str(m_RejectReason) << ")"); + << " for reject reason code " << m_RejectReason.load() << " (" << srt_rejectreason_str(m_RejectReason) << ")"); setPacketTS(r_rsppkt, steady_clock::now()); m_pSndQueue->sendto(serv_addr, r_rsppkt, m_SourceAddr); @@ -5471,7 +5471,7 @@ void * srt::CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " - << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); + << (timediff_us / 1000) << "." << sfmt(timediff_us % 1000, "03") << " ms"); #endif string why; if (self->frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) @@ -6114,8 +6114,9 @@ SRT_REJECT_REASON srt::CUDT::setupCC() HLOGC(rslog.Debug, log << CONID() << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" - << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" - << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); + << m_iFlowWindowSize.load() << " rcvrate=" << m_iDeliveryRate.load() + << "p/s (" << m_iByteDeliveryRate.load() << "B/S)" + << " rtt=" << m_iSRTT.load() << " bw=" << m_iBandwidth.load()); if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET))) { @@ -7115,7 +7116,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING double bw = Bps2Mbps(int64_t(m_iBandwidth) * m_iMaxSRTPayloadSize ); - HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)"); + HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth.load() << " buffers per second)"); #endif } return res; @@ -7777,8 +7778,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) - << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" - << std::setprecision(3) << cgwindow); + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << sfmt(cgwindow, ".3")); #endif } @@ -8425,8 +8425,9 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ const bool isLiteAck = ctrlpkt.getLength() == (size_t)SEND_LITE_ACK; HLOGC(inlog.Debug, - log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ackdata_seqno << " [ACK=" << m_iSndLastAck - << "]" << (isLiteAck ? "[LITE]" : "[FULL]")); + log << CONID() << "ACK covers: " << m_iSndLastDataAck.load() << " - " + << ackdata_seqno << " [ACK=" << m_iSndLastAck.load() << "]" + << (isLiteAck ? "[LITE]" : "[FULL]")); updateSndLossListOnACK(ackdata_seqno); @@ -8497,8 +8498,9 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ { m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); HLOGC(gglog.Debug, - log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " << m_iFlowWindowSize - << " SPAN " << getFlightSpan() << " ackdataseqno %" << ackdata_seqno); + log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " + << m_iFlowWindowSize.load() << " SPAN " << getFlightSpan() + << " ackdataseqno %" << ackdata_seqno); } } @@ -8823,7 +8825,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0) { HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding " - << m_iSndLastAck << "[ACK] - " << losslist_hi << " to loss list"); + << m_iSndLastAck.load() << "[ACK] - " << losslist_hi << " to loss list"); num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi); dropreq_hi = CSeqNo::decseq(m_iSndLastAck); IF_HEAVY_LOGGING(drop_type = "partially"); @@ -8839,8 +8841,9 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // - finally give up rexmit request as per TLPKTDROP (DROPREQ should make // TSBPD wake up should it still wait for new packets to get ACK-ed) HLOGC(inlog.Debug, - log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" << m_iSndLastAck - << ": %" << losslist_lo << "-" << dropreq_hi << " - sending DROPREQ"); + log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" + << m_iSndLastAck.load() << ": %" << losslist_lo << "-" << dropreq_hi + << " - sending DROPREQ"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); } @@ -8880,7 +8883,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) int32_t seqpair[2] = { losslist[i], losslist[i] }; const int32_t no_msgno = 0; // We don't know. HLOGC(inlog.Debug, - log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck << ": %" << losslist[i] + log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck.load() << ": %" << losslist[i] << " - sending DROPREQ"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); } @@ -9364,7 +9367,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) - << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar + << " RTT=" << m_iSRTT.load() << " RTTVar=" << m_iRTTVar.load() << " now=" << FormatTime(time_now)); continue; } @@ -9676,8 +9679,8 @@ 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.seqno() << " (ACK=" << m_iSndLastAck - << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck.load() + << " ACKDATA=" << m_iSndLastDataAck.load() << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif // Fix keepalive @@ -9757,8 +9760,8 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) if (cwnd <= flightspan) { HLOGC(qslog.Debug, - log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_iCongestionWindow - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo.load() << ")=" << flightspan); + log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize.load() << "," << m_iCongestionWindow.load() + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck.load() << "-" << m_iSndCurrSeqNo.load() << ")=" << flightspan); return false; } @@ -10013,9 +10016,9 @@ int srt::CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - std::ostringstream tns1, tns2; + fmt::obufstream tns1, tns2; // Take the last 2 ciphers from the socket ID. - tns1 << setfill('0') << setw(2) << m_SocketID; + tns1 << sfmt(m_SocketID, "02"); std::string s = tns1.str(); tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); const string thname = tns2.str(); @@ -11199,7 +11202,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason.load() << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); @@ -11231,7 +11234,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (result == -1) { hs.m_iReqType = URQFailure(error); - LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << int(hs.m_iReqType) << " - " << srt_rejectreason_str(error)); + LOGC(cnlog.Warn, log << "processConnectRequest: rsp(REJECT): " << hs.m_iReqType << " - " << srt_rejectreason_str(error)); } // The `acpu` not NULL means connection exists, the `result` should be 0. It is not checked here though. @@ -11335,7 +11338,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) } } } - LOGC(cnlog.Debug, log << CONID() << "listen ret: " << int(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/group.cpp b/srtcore/group.cpp index 5fea652d9..332b1657d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -1023,7 +1023,7 @@ int CUDTGroup::send(const char* buf, int len, SRT_MSGCTRL& w_mc) switch (m_type) { default: - LOGC(gslog.Error, log << "CUDTGroup::send: not implemented for type #" << int(m_type)); + LOGC(gslog.Error, log << "CUDTGroup::send: not implemented for type #" << m_type); throw CUDTException(MJ_SETUP, MN_INVAL, 0); case SRT_GTYPE_BROADCAST: diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 98999a81f..aefcea001 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -139,7 +139,7 @@ srt::CUnitQueue::CQEntry* srt::CUnitQueue::allocateEntry(const int iNumUnits, co int srt::CUnitQueue::increase_() { const int numUnits = m_iBlockSize; - HLOGC(qrlog.Debug, log << "CUnitQueue::increase: Capacity" << capacity() << " + " << numUnits << " new units, " << m_iNumTaken << " in use."); + HLOGC(qrlog.Debug, log << "CUnitQueue::increase: Capacity" << capacity() << " + " << numUnits << " new units, " << m_iNumTaken.load() << " in use."); CQEntry* tempq = allocateEntry(numUnits, m_iMSS); if (tempq == NULL) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 65632ad9a..f635a1020 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -300,14 +300,14 @@ form_memory_buffer<> fix_format(const char* fmt, #define SFMT_FORMAT_FIXER(TYPE, ALLOWED, TYPED, DEFTYPE, WARN) \ -form_memory_buffer<> apply_format_fix(TYPE, const char* fmt) \ +inline form_memory_buffer<> apply_format_fix(TYPE, const char* fmt) \ { \ return fix_format(fmt, ALLOWED, TYPED, DEFTYPE, WARN); \ } #define SFMT_FORMAT_FIXER_TPL(TPAR, TYPE, ALLOWED, TYPED, DEFTYPE, WARN) \ template\ -form_memory_buffer<> apply_format_fix(TYPE, const char* fmt)\ +inline form_memory_buffer<> apply_format_fix(TYPE, const char* fmt)\ {\ return fix_format(fmt, ALLOWED, TYPED, DEFTYPE, WARN); \ } @@ -592,7 +592,7 @@ internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) // doesn't do it an doesn't use the NUL-termination. fstr.append('\0'); - size_t valsize = SNPrintfOne( buf, bufsize, fstr.get_first(), val); + size_t valsize = SNPrintfOne(buf, bufsize, fstr.get_first(), val); // Deemed impossible to happen, but still if (valsize == bufsize) @@ -601,7 +601,7 @@ internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) // Just try again with one extra size, if this won't // suffice, just add <...> at the end. buf = out.expose(bufsize); - valsize = snprintf(buf, bufsize, fstr.get_first(), val); + valsize = SNPrintfOne(buf, bufsize, fstr.get_first(), val); if (valsize == bufsize) { char* end = buf + bufsize - 6; diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 96344f0b2..d89cc0211 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -82,9 +82,10 @@ void TargetMedium::Runner() return; } + const char* yesno[2] = {"no", "yes"}; bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:", - boolalpha, gotsomething, " running: ", running, ")"); + yesno[!gotsomething], " running: ", running.load(), ")"); if (::transmit_int_state || !running || !med || med->Broken()) { LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ", diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index 011dcbfe7..67782edbd 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -151,7 +151,7 @@ struct TargetMedium: Medium { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); std::lock_guard lg(buffer_lock); - LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); + LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running.load(), " interrupt=", ::transmit_int_state.load()); if (!running || ::transmit_int_state) { LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); From e37d37dab19c7c530397a55ab59b27fd788f71e6 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Sun, 26 May 2024 15:03:30 +0200 Subject: [PATCH 127/517] Provided a special version for atomic. Withdrawn unnecessary changes --- srtcore/buffer_snd.cpp | 6 +-- srtcore/congctl.cpp | 8 ++-- srtcore/core.cpp | 91 ++++++++++++++++++------------------- srtcore/group.cpp | 2 +- srtcore/logging.h | 65 ++++++++++++++++++-------- srtcore/queue.cpp | 2 +- srtcore/socketconfig.cpp | 10 ++-- testing/testactivemedia.cpp | 2 +- testing/testactivemedia.hpp | 2 +- 9 files changed, 107 insertions(+), 81 deletions(-) diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index 901e829b6..ef1bc498c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -141,7 +141,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) const int iNumBlocks = countNumPacketsRequired(len, iPktLen); HLOGC(bslog.Debug, - log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount.load() << "/" << m_iSize); + log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize); // Retrieve current time before locking the mutex to be closer to packet submission event. const steady_clock::time_point tnow = steady_clock::now(); @@ -242,7 +242,7 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) const int iNumBlocks = countNumPacketsRequired(len, iPktLen); HLOGC(bslog.Debug, - log << "addBufferFromFile: size=" << m_iCount.load() << " reserved=" << m_iSize << " needs=" << iPktLen + log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen << " buffers for " << len << " bytes"); // dynamically increase sender buffer @@ -393,7 +393,7 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) { // Prevent accessing the last "marker" block LOGC(bslog.Error, - log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount.load()); + log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount); return SRT_MSGNO_CONTROL; } diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index bd4c9f162..0f6edb15b 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -87,7 +87,7 @@ class LiveCC: public SrtCongestionControlBase m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec) m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2) - HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize.load()); + HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize); updatePktSndPeriod(); @@ -154,7 +154,7 @@ class LiveCC: public SrtCongestionControlBase // thread will pick up a "slightly outdated" average value from this // field - this is insignificant. m_zSndAvgPayloadSize = avg_iir<128, size_t>(m_zSndAvgPayloadSize, packet.getLength()); - HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize.load()); + HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize); } /// @brief On RTO event update an inter-packet send interval. @@ -179,7 +179,7 @@ class LiveCC: public SrtCongestionControlBase const double pktsize = (double) m_zSndAvgPayloadSize.load() + m_zHeaderSize; m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW); HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod - << " by avg pktsize=" << m_zSndAvgPayloadSize.load() + << " by avg pktsize=" << m_zSndAvgPayloadSize << ", bw=" << m_llSndMaxBW); } @@ -595,7 +595,7 @@ class FileCC : public SrtCongestionControlBase { m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << fmt::sfmt(m_dCWndSize, "06") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); + << fmt::sfmt(m_dCWndSize, ".6") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 00ab9367b..f87692caa 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -88,11 +88,13 @@ using namespace std; using namespace srt; using namespace srt::sync; using namespace srt_logging; -using namespace fmt; +using fmt::sfmt; const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; +static const char onoff[2] = {'-', '+'}; + //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ #define SRT_CMD_HSREQ_SZ 12 /* Current version packet size */ @@ -1772,7 +1774,6 @@ bool srt::CUDT::createSrtHandshake( if (!m_pCryptoControl && (srtkm_cmd == SRT_CMD_KMREQ || srtkm_cmd == SRT_CMD_KMRSP)) { m_RejectReason = SRT_REJ_IPE; - static const char onoff[2] = {'-', '+'}; LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." << " Socket state: " @@ -3457,7 +3458,7 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { HLOGC(gmlog.Debug, log << CONID() << "synchronizeWithGroup: DERIVED ISN: RCV=%" << m_iRcvLastAck << " -> %" << rcv_isn - << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck.load() + << " (shift by " << CSeqNo::seqcmp(rcv_isn, m_iRcvLastAck) << ") SND=%" << m_iSndLastAck << " -> %" << snd_isn << " (shift by " << CSeqNo::seqcmp(snd_isn, m_iSndLastAck) << ")"); setInitialRcvSeq(rcv_isn); setInitialSndSeq(snd_isn); @@ -3466,7 +3467,7 @@ void srt::CUDT::synchronizeWithGroup(CUDTGroup* gp) { HLOGC(gmlog.Debug, log << CONID() << "synchronizeWithGroup: DEFINED ISN: RCV=%" << m_iRcvLastAck << " SND=%" - << m_iSndLastAck.load()); + << m_iSndLastAck); } } #endif @@ -3883,7 +3884,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) HLOGC(cnlog.Debug, log << CONID() << "startConnect: END. Parameters: mss=" << m_config.iMSS << " max-cwnd-size=" << m_CongCtl->cgWindowMaxSize() << " cwnd-size=" << m_CongCtl->cgWindowSize() - << " rtt=" << m_iSRTT.load() << " bw=" << m_iBandwidth.load()); + << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); } // Asynchronous connection @@ -4022,7 +4023,7 @@ void srt::CUDT::sendRendezvousRejection(const sockaddr_any& serv_addr, CPacket& r_rsppkt.setLength(size); HLOGC(cnlog.Debug, log << CONID() << "sendRendezvousRejection: using code=" << m_ConnReq.m_iReqType - << " for reject reason code " << m_RejectReason.load() << " (" << srt_rejectreason_str(m_RejectReason) << ")"); + << " for reject reason code " << m_RejectReason << " (" << srt_rejectreason_str(m_RejectReason) << ")"); setPacketTS(r_rsppkt, steady_clock::now()); m_pSndQueue->sendto(serv_addr, r_rsppkt, m_SourceAddr); @@ -4104,7 +4105,6 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas // m_pCryptoControl can be NULL if the socket has been closed already. See issue #2231. if (!m_pCryptoControl) { - static const char onoff[2] = {'-', '+'}; m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Error, log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." @@ -6114,9 +6114,8 @@ SRT_REJECT_REASON srt::CUDT::setupCC() HLOGC(rslog.Debug, log << CONID() << "setupCC: setting parameters: mss=" << m_config.iMSS << " maxCWNDSize/FlowWindowSize=" - << m_iFlowWindowSize.load() << " rcvrate=" << m_iDeliveryRate.load() - << "p/s (" << m_iByteDeliveryRate.load() << "B/S)" - << " rtt=" << m_iSRTT.load() << " bw=" << m_iBandwidth.load()); + << m_iFlowWindowSize << " rcvrate=" << m_iDeliveryRate << "p/s (" << m_iByteDeliveryRate << "B/S)" + << " rtt=" << m_iSRTT << " bw=" << m_iBandwidth); if (!updateCC(TEV_INIT, EventVariant(TEV_INIT_RESET))) { @@ -6585,7 +6584,7 @@ int srt::CUDT::sndDropTooLate() } HLOGC(qslog.Debug, - log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo.load() << ") n=" << dpkts << "pkt " << dbytes + log << CONID() << "SND-DROP: %(" << realack << "-" << m_iSndCurrSeqNo << ") n=" << dpkts << "pkt " << dbytes << "B, span=" << buffdelay_ms << " ms, FIRST #" << first_msgno); #if ENABLE_BONDING @@ -7116,7 +7115,7 @@ int srt::CUDT::receiveMessage(char* data, int len, SRT_MSGCTRL& w_mctrl, int by_ // After signaling the tsbpd for ready data, report the bandwidth. #if ENABLE_HEAVY_LOGGING double bw = Bps2Mbps(int64_t(m_iBandwidth) * m_iMaxSRTPayloadSize ); - HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth.load() << " buffers per second)"); + HLOGC(arlog.Debug, log << CONID() << "CURRENT BANDWIDTH: " << bw << "Mbps (" << m_iBandwidth << " buffers per second)"); #endif } return res; @@ -8425,9 +8424,8 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ const bool isLiteAck = ctrlpkt.getLength() == (size_t)SEND_LITE_ACK; HLOGC(inlog.Debug, - log << CONID() << "ACK covers: " << m_iSndLastDataAck.load() << " - " - << ackdata_seqno << " [ACK=" << m_iSndLastAck.load() << "]" - << (isLiteAck ? "[LITE]" : "[FULL]")); + log << CONID() << "ACK covers: " << m_iSndLastDataAck << " - " << ackdata_seqno << " [ACK=" << m_iSndLastAck + << "]" << (isLiteAck ? "[LITE]" : "[FULL]")); updateSndLossListOnACK(ackdata_seqno); @@ -8477,7 +8475,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // this should not happen: attack or bug LOGC(gglog.Error, log << CONID() << "ATTACK/IPE: incoming ack seq " << ackdata_seqno << " exceeds current " - << m_iSndCurrSeqNo.load() << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); + << m_iSndCurrSeqNo << " by " << (CSeqNo::seqoff(m_iSndCurrSeqNo, ackdata_seqno) - 1) << "!"); m_bBroken = true; m_iBrokenCounter = 0; return; @@ -8498,9 +8496,8 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ { m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); HLOGC(gglog.Debug, - log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " - << m_iFlowWindowSize.load() << " SPAN " << getFlightSpan() - << " ackdataseqno %" << ackdata_seqno); + log << CONID() << "processCtrlAck: could reschedule SND. iFlowWindowSize " << m_iFlowWindowSize + << " SPAN " << getFlightSpan() << " ackdataseqno %" << ackdata_seqno); } } @@ -8680,14 +8677,14 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr 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.load() << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } LOGC(inlog.Error, log << CONID() << "ACK record not found, can't estimate RTT " << "(ACK number: " << ctrlpkt.getAckSeqNo() << ", last ACK sent: " << m_iAckSeqNo - << ", RTT (EWMA): " << m_iSRTT.load() << ")"); + << ", RTT (EWMA): " << m_iSRTT << ")"); return; } @@ -8790,7 +8787,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // LO must not be greater than HI. // HI must not be greater than the most recent sent seq. LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT rng " << losslist_lo << " - " << losslist_hi - << " with last sent " << m_iSndCurrSeqNo.load() << " - DISCARDING"); + << " with last sent " << m_iSndCurrSeqNo << " - DISCARDING"); secure = false; wrong_loss = losslist_hi; break; @@ -8825,7 +8822,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (CSeqNo::seqcmp(losslist_hi, m_iSndLastAck) >= 0) { HLOGC(inlog.Debug, log << CONID() << "LOSSREPORT: adding " - << m_iSndLastAck.load() << "[ACK] - " << losslist_hi << " to loss list"); + << m_iSndLastAck << "[ACK] - " << losslist_hi << " to loss list"); num = m_pSndLossList->insert(m_iSndLastAck, losslist_hi); dropreq_hi = CSeqNo::decseq(m_iSndLastAck); IF_HEAVY_LOGGING(drop_type = "partially"); @@ -8841,9 +8838,8 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) // - finally give up rexmit request as per TLPKTDROP (DROPREQ should make // TSBPD wake up should it still wait for new packets to get ACK-ed) HLOGC(inlog.Debug, - log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" - << m_iSndLastAck.load() << ": %" << losslist_lo << "-" << dropreq_hi - << " - sending DROPREQ"); + log << CONID() << "LOSSREPORT: " << drop_type << " IGNORED with SndLastAck=%" << m_iSndLastAck + << ": %" << losslist_lo << "-" << dropreq_hi << " - sending DROPREQ"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); } @@ -8860,7 +8856,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (CSeqNo::seqcmp(losslist[i], m_iSndCurrSeqNo) > 0) { LOGC(inlog.Warn, log << CONID() << "rcv LOSSREPORT pkt %" << losslist[i] - << " with last sent %" << m_iSndCurrSeqNo.load() << " - DISCARDING"); + << " with last sent %" << m_iSndCurrSeqNo << " - DISCARDING"); // loss_seq must not be greater than the most recent sent seq secure = false; wrong_loss = losslist[i]; @@ -8883,7 +8879,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) int32_t seqpair[2] = { losslist[i], losslist[i] }; const int32_t no_msgno = 0; // We don't know. HLOGC(inlog.Debug, - log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck.load() << ": %" << losslist[i] + log << CONID() << "LOSSREPORT: IGNORED with SndLastAck=%" << m_iSndLastAck << ": %" << losslist[i] << " - sending DROPREQ"); sendCtrl(UMSG_DROPREQ, &no_msgno, seqpair, sizeof(seqpair)); } @@ -8896,7 +8892,7 @@ void srt::CUDT::processCtrlLossReport(const CPacket& ctrlpkt) if (!secure) { LOGC(inlog.Warn, - log << CONID() << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo.load() + log << CONID() << "out-of-band LOSSREPORT received; BUG or ATTACK - last sent %" << m_iSndCurrSeqNo << " vs loss %" << wrong_loss); // this should not happen: attack or bug m_bBroken = true; @@ -9088,7 +9084,7 @@ void srt::CUDT::processCtrlDropReq(const CPacket& ctrlpkt) else { HLOGC(inlog.Debug, log << CONID() << "DROPREQ: dropping %" - << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo.load()); + << dropdata[0] << "-" << dropdata[1] << " current %" << m_iRcvCurrSeqNo); } } @@ -9336,7 +9332,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) // was completely ignored. LOGC(qrlog.Error, log << CONID() << "IPE/EPE: packLostData: LOST packet negative offset: seqoff(seqno() " - << w_packet.seqno() << ", m_iSndLastDataAck " << m_iSndLastDataAck.load() << ")=" << offset + << 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 @@ -9367,7 +9363,7 @@ int srt::CUDT::packLostData(CPacket& w_packet) { HLOGC(qrlog.Debug, log << CONID() << "REXMIT: ignoring seqno " << w_packet.seqno() << ", last rexmit " << (is_zero(tsLastRexmit) ? "never" : FormatTime(tsLastRexmit)) - << " RTT=" << m_iSRTT.load() << " RTTVar=" << m_iRTTVar.load() + << " RTT=" << m_iSRTT << " RTTVar=" << m_iRTTVar << " now=" << FormatTime(time_now)); continue; } @@ -9679,8 +9675,8 @@ 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.seqno() << " (ACK=" << m_iSndLastAck.load() - << " ACKDATA=" << m_iSndLastDataAck.load() << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck + << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif // Fix keepalive @@ -9760,8 +9756,8 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) if (cwnd <= flightspan) { HLOGC(qslog.Debug, - log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize.load() << "," << m_iCongestionWindow.load() - << ")=" << cwnd << " seqlen=(" << m_iSndLastAck.load() << "-" << m_iSndCurrSeqNo.load() << ")=" << flightspan); + log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_iCongestionWindow + << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); return false; } @@ -9777,7 +9773,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { // Some packets were skipped due to TTL expiry. m_iSndCurrSeqNo = CSeqNo::incseq(m_iSndCurrSeqNo, pktskipseqno); - HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo.load() + HLOGC(qslog.Debug, log << "packUniqueData: reading skipped " << pktskipseqno << " seq up to %" << m_iSndCurrSeqNo << " due to TTL expiry"); } @@ -9981,7 +9977,7 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) if (diff < 0 || diff > CSeqNo::m_iSeqNoTH) { LOGC(gslog.Error, log << CONID() << "IPE: Overriding with seq %" << seq << " DISCREPANCY against current %" - << m_iSndCurrSeqNo.load() << " and next sched %" << m_iSndNextSeqNo.load() << " - diff=" << diff); + << m_iSndCurrSeqNo << " and next sched %" << m_iSndNextSeqNo << " - diff=" << diff); return false; } @@ -9998,7 +9994,7 @@ bool srt::CUDT::overrideSndSeqNo(int32_t seq) // not yet sent. HLOGC(gslog.Debug, - log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo.load() << " send-seq=" << m_iSndCurrSeqNo.load() + log << CONID() << "overrideSndSeqNo: sched-seq=" << m_iSndNextSeqNo << " send-seq=" << m_iSndCurrSeqNo << " (unchanged)"); return true; } @@ -10016,12 +10012,11 @@ int srt::CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - fmt::obufstream tns1, tns2; + fmt::obufstream buf; // Take the last 2 ciphers from the socket ID. - tns1 << sfmt(m_SocketID, "02"); - std::string s = tns1.str(); - tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); - const string thname = tns2.str(); + string s = fmt::sfmts(m_SocketID, "02"); + buf << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); + const string thname = buf.str(); #else const string thname = "SRT:TsbPd"; #endif @@ -10121,7 +10116,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& "SEQUENCE DISCREPANCY. BREAKING CONNECTION." " %" << rpkt.seqno() << " buffer=(%" << bufseq - << ":%" << m_iRcvCurrSeqNo.load() // -1 = size to last index + << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) << "), " << (m_pRcvBuffer->capacity() - bufidx + 1) << " past max. Reception no longer possible. REQUESTING TO CLOSE."); @@ -10241,7 +10236,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& bufinfo << " BUF.s=" << m_pRcvBuffer->capacity() << " avail=" << (int(m_pRcvBuffer->capacity()) - ackidx) << " buffer=(%" << bufseq - << ":%" << m_iRcvCurrSeqNo.load() // -1 = size to last index + << ":%" << m_iRcvCurrSeqNo // -1 = size to last index << "+%" << CSeqNo::incseq(bufseq, int(m_pRcvBuffer->capacity()) - 1) << ")"; } @@ -10739,7 +10734,7 @@ void srt::CUDT::updateIdleLinkFrom(CUDT* source) { // Reject the change because that would shift the reception pointer backwards. HLOGC(grlog.Debug, log << "grp: NOT updating rcv-seq in @" << m_SocketID - << ": backward setting rejected: %" << m_iRcvCurrSeqNo.load() + << ": backward setting rejected: %" << m_iRcvCurrSeqNo << " -> %" << new_last_rcv); return; } @@ -11202,7 +11197,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (!accepted_hs) { HLOGC(cnlog.Debug, - log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason.load() + log << CONID() << "processConnectRequest: version/type mismatch. Sending REJECT code:" << m_RejectReason << " MSG: " << srt_rejectreason_str(m_RejectReason)); // mismatch, reject the request hs.m_iReqType = URQFailure(m_RejectReason); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 332b1657d..286d65de5 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -2250,7 +2250,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { LOGC(grlog.Error, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": SEQUENCE DISCREPANCY: base=%" - << m_RcvBaseSeqNo.load() << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); + << m_RcvBaseSeqNo << " vs pkt=%" << info.seqno << ", setting ESECFAIL"); ps->core().m_bBroken = true; broken.insert(ps); continue; diff --git a/srtcore/logging.h b/srtcore/logging.h index 0917af9ac..0953a397d 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -337,6 +337,43 @@ struct LogDispatcher::Proxy return *this; } + // Special case for atomics, as passing them to snprintf() call + // requires unpacking the real underlying value. + template + Proxy& operator<<(const srt::sync::atomic& arg) + { + if ( that_enabled ) + { + os << arg.load(); + } + return *this; + } + + +#if HAVE_CXX11 + + void dispatch() {} + + template + void dispatch(const Arg1& a1, const Args&... others) + { + *this << a1; + dispatch(others...); + } + + // Special dispatching for atomics must be provided here. + // By some reason, "*this << a1" expression gets dispatched + // to the general version of operator<<, not the overload for + // atomic. Even though the compiler shows Arg1 type as atomic. + template + void dispatch(const std::atomic& a1, const Args&... others) + { + *this << a1.load(); + dispatch(others...); + } + +#endif + ~Proxy() { if ( that_enabled ) @@ -445,19 +482,19 @@ inline void PrintArgs(fmt::obufstream& serr, Arg1&& arg1, Args&&... args) PrintArgs(serr, args...); } +// Add exceptional handling for sync::atomic +template +inline void PrintArgs(fmt::obufstream& serr, const srt::sync::atomic& arg1, Args&&... args) +{ + serr << arg1.load(); + PrintArgs(serr, args...); +} + template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - fmt::obufstream serr; - CreateLogLinePrefix(serr); - PrintArgs(serr, args...); - - if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << fmt::seol; - - // Not sure, but it wasn't ever used. - SendLogLine(file, line, area, serr.str()); + Proxy(*this).dispatch(args...); #endif } @@ -467,15 +504,7 @@ template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - fmt::obufstream serr; - CreateLogLinePrefix(serr); - serr << arg; - - if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << fmt::seol; - - // Not sure, but it wasn't ever used. - SendLogLine(file, line, area, serr.str()); + Proxy(*this) << arg; #endif } diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index aefcea001..98999a81f 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -139,7 +139,7 @@ srt::CUnitQueue::CQEntry* srt::CUnitQueue::allocateEntry(const int iNumUnits, co int srt::CUnitQueue::increase_() { const int numUnits = m_iBlockSize; - HLOGC(qrlog.Debug, log << "CUnitQueue::increase: Capacity" << capacity() << " + " << numUnits << " new units, " << m_iNumTaken.load() << " in use."); + HLOGC(qrlog.Debug, log << "CUnitQueue::increase: Capacity" << capacity() << " + " << numUnits << " new units, " << m_iNumTaken << " in use."); CQEntry* tempq = allocateEntry(numUnits, m_iMSS); if (tempq == NULL) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 9c5a9513e..cec4845b4 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -52,6 +52,8 @@ written by #include "srt.h" #include "socketconfig.h" +using fmt::sfmt; + namespace srt { int RcvBufferSizeOptionToValue(int val, int flightflag, int mss) @@ -744,8 +746,8 @@ struct CSrtConfigSetter { co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << fmt::sfmt(km_refresh, "x") << ": setting SRTO_KMPREANNOUNCE=0x" - << fmt::sfmt(co.uKmPreAnnouncePkt, "x")); + log << "SRTO_KMREFRESHRATE=0x" << sfmt(km_refresh, "x") << ": setting SRTO_KMPREANNOUNCE=0x" + << sfmt(co.uKmPreAnnouncePkt, "x")); } } }; @@ -770,8 +772,8 @@ struct CSrtConfigSetter if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << fmt::sfmt(km_preanno, "x") - << " exceeds KmRefresh/2, 0x" << fmt::sfmt((kmref - 1) / 2) + log << "SRTO_KMPREANNOUNCE=0x" << sfmt(km_preanno, "x") + << " exceeds KmRefresh/2, 0x" << sfmt((kmref - 1) / 2, "x") << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index d89cc0211..332a9fd6f 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -85,7 +85,7 @@ void TargetMedium::Runner() const char* yesno[2] = {"no", "yes"}; bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:", - yesno[!gotsomething], " running: ", running.load(), ")"); + yesno[!gotsomething], " running: ", running, ")"); if (::transmit_int_state || !running || !med || med->Broken()) { LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ", diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index 67782edbd..011dcbfe7 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -151,7 +151,7 @@ struct TargetMedium: Medium { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); std::lock_guard lg(buffer_lock); - LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running.load(), " interrupt=", ::transmit_int_state.load()); + LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); if (!running || ::transmit_int_state) { LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); From 6ce84feace50ac1fb2990759df99f3978524b8f0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Sun, 26 May 2024 16:04:54 +0200 Subject: [PATCH 128/517] Changed more formatting usage to sfmt --- srtcore/api.cpp | 4 ++-- srtcore/queue.cpp | 4 ++-- srtcore/sfmt.h | 53 +++++++++++++++++++++++++++++++++++++++++++++ srtcore/sync.cpp | 22 +++++++++++++------ srtcore/sync.h | 6 +++-- srtcore/utilities.h | 22 ++++++++++++++----- 6 files changed, 93 insertions(+), 18 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 56c581fec..62bbd787b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -231,7 +231,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) if (sock == 0) return ""; - std::ostringstream os; + fmt::obufstream os; os << "@" << sock << ":"; return os.str(); } @@ -3240,7 +3240,7 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer& m = i->second; #if ENABLE_HEAVY_LOGGING - ostringstream that_muxer; + fmt::obufstream that_muxer; that_muxer << "id=" << m.m_iID << " port=" << m.m_iPort << " ip=" << (m.m_iIPversion == AF_INET ? "v4" : "v6"); #endif diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 98999a81f..78de6a6b9 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1078,8 +1078,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, else { HLOGC(cnlog.Debug, - log << "RID: socket @" << i->m_iID << " still active (remaining " << std::fixed - << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + log << "RID: socket @" << i->m_iID << " still active (remaining " + << fmt::sfmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, "f") << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index f635a1020..367926bce 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -10,6 +10,9 @@ // for FILE type from stdio. It has nothing to do with the rest of the {fmt} // library, except that it reuses the namespace. +#ifndef INC_FMT_SFMT_H +#define INC_FMT_SFMT_H + #include #include #include @@ -490,6 +493,19 @@ class obufstream return *this; } + // For unusual manipulation, usually to add NUL termination. + // NOTE: you must make sure that you won't use the extended + // buffers if the intention was to get a string. + void append(char c) + { + buffer.append(c); + } + + const char* bufptr() const + { + return buffer.get_first(); + } + template obufstream& operator<<(const internal::form_memory_buffer& b) { @@ -558,6 +574,41 @@ class obufstream std::copy(data, data + i->size(), std::back_inserter(out)); } } + + template + size_t copy_to(OutputContainer& out, size_t maxsize) const + { + using namespace internal; + size_t avail = maxsize; + if (avail < buffer.first_size()) + { + std::copy(buffer.get_first(), buffer.get_first() + avail, + std::back_inserter(out)); + return maxsize; + } + + std::copy(buffer.get_first(), buffer.get_first() + buffer.first_size(), + std::back_inserter(out)); + + avail -= buffer.first_size(); + + for (form_memory_buffer<>::slices_t::const_iterator i = buffer.get_slices().begin(); + i != buffer.get_slices().end(); ++i) + { + // Would be tempting to move the blocks, but C++03 doesn't feature moving. + const char* data = &(*i)[0]; + + if (avail < i->size()) + { + std::copy(data, data + avail, std::back_inserter(out)); + return maxsize; + } + std::copy(data, data + i->size(), std::back_inserter(out)); + avail -= i->size(); + } + + return maxsize - avail; + } }; namespace internal @@ -662,3 +713,5 @@ inline ostdiostream& operator<<(ostdiostream& sout, const os_flush_manip&) } + +#endif diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index a7cebb909..e4e511fb5 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -50,13 +50,21 @@ std::string FormatTime(const steady_clock::time_point& timestamp) const uint64_t hours = total_sec / (60 * 60) - days * 24; const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; - ostringstream out; + + // Temporary solution. Need to find some better handling + // of dynamic width and precision. + fmt::obufstream dfmts; + dfmts << "0" << decimals; + dfmts.append('\0'); // form_memory_buffer doesn't use NUL-termination. + const char* decimal_fmt = dfmts.bufptr(); + + fmt::obufstream out; if (days) out << days << "D "; - out << setfill('0') << setw(2) << hours << ":" - << setfill('0') << setw(2) << minutes << ":" - << setfill('0') << setw(2) << seconds << "." - << setfill('0') << setw(decimals) << (timestamp - seconds_from(total_sec)).time_since_epoch().count() << " [STDY]"; + out << fmt::sfmt(hours, "02") << ":" + << fmt::sfmt(minutes, "02") << ":" + << fmt::sfmt(seconds, "02") << "." + << fmt::sfmt((timestamp - seconds_from(total_sec)).time_since_epoch().count(), decimal_fmt) << " [STDY]"; return out.str(); } @@ -72,8 +80,8 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) char tmp_buf[512]; strftime(tmp_buf, 512, "%X.", &tm); - ostringstream out; - out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYST]"; + fmt::obufstream out; + out << tmp_buf << fmt::sfmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, "06") << " [SYST]"; return out.str(); } diff --git a/srtcore/sync.h b/srtcore/sync.h index fb6d56432..627f7eac5 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -55,7 +55,7 @@ #include "srt.h" #include "utilities.h" #include "srt_attr_defs.h" - +#include "sfmt.h" namespace srt { @@ -775,7 +775,9 @@ struct DurationUnitName template inline std::string FormatDuration(const steady_clock::duration& dur) { - return Sprint(std::fixed, DurationUnitName::count(dur)) + DurationUnitName::name(); + fmt::obufstream out; + out << fmt::sfmt(DurationUnitName::count(dur), "f") << DurationUnitName::name(); + return out.str(); } inline std::string FormatDuration(const steady_clock::duration& dur) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 1786cf0ae..ef5e41d81 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -34,7 +34,6 @@ written by #include #include #include -#include #include #if HAVE_CXX11 @@ -46,6 +45,8 @@ written by #include #include +#include "sfmt.h" + // -------------- UTILITIES ------------------------ // --- ENDIAN --- @@ -981,6 +982,19 @@ inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) if ( size == 0 ) return ""; + using namespace fmt; + + obufstream os; + + os << sfmt(bytes[0], "02X"); + for (size_t i = 1; i < size; ++i) + { + os << sfmt(bytes[i], "02X"); + } + return os.str(); + + /* OLD VERSION + //char buf[256]; using namespace std; @@ -1008,6 +1022,7 @@ inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) os << int(bytes[i]); } return os.str(); + */ } @@ -1171,10 +1186,7 @@ inline std::string BufferStamp(const char* mem, size_t size) } // Convert to hex string - ostringstream os; - os << hex << uppercase << setfill('0') << setw(8) << sum; - - return os.str(); + return fmt::sfmts(sum, "08X"); } template From 8126c49f91c1cd675fda6a76824d238b6e5d126b Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Sun, 26 May 2024 20:42:30 +0200 Subject: [PATCH 129/517] Removed ostringstream from utilities --- srtcore/utilities.h | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index ef5e41d81..697e43e08 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -485,7 +485,7 @@ class FixedArray void throw_invalid_index(int i) const { - std::stringstream ss; + fmt::obufstream ss; ss << "Index " << i << "out of range"; throw std::runtime_error(ss.str()); } @@ -587,7 +587,7 @@ inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) template inline std::string Sprint(Args&&... args) { - std::ostringstream sout; + fmt::obufstream sout; Print(sout, args...); return sout.str(); } @@ -598,19 +598,32 @@ inline std::string Sprint(Args&&... args) template using UniquePtr = std::unique_ptr; -template inline -std::string Printable(const Container& in, Value /*pseudoargument*/, Args&&... args) +template inline +std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) { using namespace srt_pair_op; - std::ostringstream os; - Print(os, args...); + fmt::obufstream os; os << "[ "; for (auto i: in) - os << Value(i) << " "; + os << fmt::sfmt(i, fmt) << " "; os << "]"; return os.str(); } +// Separate version for pairs, used for std::map +template inline +std::string Printable(const Container& in, std::pair/*pseudoargument*/, const char* fmtk = 0, const char* fmtv = 0) +{ + using namespace srt_pair_op; + fmt::obufstream os; + os << "[ "; + for (auto i: in) + os << fmt::sfmt(i.first, fmtk) << ":" << fmt::sfmt(i.second, fmtv) << " "; + os << "]"; + return os.str(); +} + + template inline std::string Printable(const Container& in) { @@ -694,16 +707,14 @@ class UniquePtr: public std::auto_ptr template inline std::string Sprint(const Arg1& arg) { - std::ostringstream sout; - sout << arg; - return sout.str(); + return fmt::sfmts(arg); } // Ok, let it be 2-arg, in case when a manipulator is needed template inline std::string Sprint(const Arg1& arg1, const Arg2& arg2) { - std::ostringstream sout; + fmt::obufstream sout; sout << arg1 << arg2; return sout.str(); } @@ -713,7 +724,7 @@ std::string Printable(const Container& in) { using namespace srt_pair_op; typedef typename Container::value_type Value; - std::ostringstream os; + fmt::obufstream os; os << "[ "; for (typename Container::const_iterator i = in.begin(); i != in.end(); ++i) os << Value(*i) << " "; @@ -759,7 +770,7 @@ std::string PrintableMod(const Container& in, const std::string& prefix) { using namespace srt_pair_op; typedef typename Container::value_type Value; - std::ostringstream os; + fmt::obufstream os; os << "[ "; for (typename Container::const_iterator y = in.begin(); y != in.end(); ++y) os << prefix << Value(*y) << " "; From 216c1ed489b654f8c760f1cabec936e9f6ca74bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 27 May 2024 08:06:12 +0200 Subject: [PATCH 130/517] Removed ostringstream use. Fixed C++03 problem with Ensure declaration --- apps/logsupport.cpp | 2 +- apps/uriparser.cpp | 2 +- srtcore/sfmt.h | 6 +++++- srtcore/utilities.h | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index fbd70c47e..7192f4596 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -173,7 +173,7 @@ set SrtParseLogFA(string fa, set* punknown) void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) { - std::ostringstream son, soff; + fmt::obufstream son, soff; for (auto& s: speclist) { diff --git a/apps/uriparser.cpp b/apps/uriparser.cpp index 6b8c80713..47e8f6a7f 100644 --- a/apps/uriparser.cpp +++ b/apps/uriparser.cpp @@ -64,7 +64,7 @@ string UriParser::makeUri() prefix = m_proto + "://"; } - std::ostringstream out; + fmt::obufstream out; out << prefix << m_host; if ((m_port == "" || m_port == "0") && m_expect == EXPECT_FILE) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 367926bce..61bbd8914 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -242,7 +242,11 @@ struct Ensure template struct Ensure { - typename AnyType::wrong_condition v = AnyType::wrong_condition; + static void CheckIfCharsetNonEmpty() + { + typename AnyType::wrong_condition v = AnyType::wrong_condition; + (void)v; + } }; template inline diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 697e43e08..b82beb76f 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -531,8 +531,8 @@ struct explicit_t // but this function has a different definition for C++11 and C++03. namespace srt_pair_op { - template - std::ostream& operator<<(std::ostream& s, const std::pair& v) + template + Stream& operator<<(Stream& s, const std::pair& v) { s << "{" << v.first << " " << v.second << "}"; return s; From d8cebcdd4b5588f8e60e0a160562c66a5b216f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 27 May 2024 09:58:04 +0200 Subject: [PATCH 131/517] Cleared out warn-errors for logging-off version --- srtcore/group.cpp | 7 ++++--- srtcore/queue.h | 4 ++++ testing/testactivemedia.cpp | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 286d65de5..0333f7408 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -15,6 +15,8 @@ extern const int32_t SRT_DEF_VERSION; namespace srt { +static inline char fmt_onoff(bool val) { return val ? '+' : '-'; } + int32_t CUDTGroup::s_tokenGen = 0; // [[using locked(this->m_GroupLock)]]; @@ -2189,10 +2191,9 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { if (!m_bOpened || !m_bConnected) { - const char onoff[2] = {'-', '+'}; LOGC(grlog.Error, - log << "grp/recv: $" << id() << ": ABANDONING: opened" << onoff[m_bOpened] - << " connected" << onoff[m_bConnected]); + log << "grp/recv: $" << id() << ": ABANDONING: opened" << fmt_onoff(m_bOpened) + << " connected" << fmt_onoff(m_bConnected)); throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); } diff --git a/srtcore/queue.h b/srtcore/queue.h index dd68a7721..055671e95 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -592,6 +592,10 @@ struct CMultiplexer , m_pRcvQueue(NULL) , m_pChannel(NULL) , m_pTimer(NULL) + , m_iPort(0) + , m_iIPversion(0) + , m_iRefCount(1) + , m_iID(-1) { } diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 332a9fd6f..9f2f03c98 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -3,6 +3,11 @@ using namespace std; +#if ENABLE_LOGGING +namespace { +const char* fmt_yesno(bool b) { return b ? "yes" : "no"; } +} +#endif void SourceMedium::Runner() { @@ -82,10 +87,9 @@ void TargetMedium::Runner() return; } - const char* yesno[2] = {"no", "yes"}; bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } ); LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:", - yesno[!gotsomething], " running: ", running, ")"); + fmt_yesno(!gotsomething), " running: ", running, ")"); if (::transmit_int_state || !running || !med || med->Broken()) { LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ", From 4e39fd7a628a5acae905c4e121fb34dd569d8059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 27 May 2024 12:01:24 +0200 Subject: [PATCH 132/517] Moved Printable out of C++11-dependent section (args... no longer needed) --- srtcore/utilities.h | 83 +++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index b82beb76f..83405b44c 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -598,40 +598,6 @@ inline std::string Sprint(Args&&... args) template using UniquePtr = std::unique_ptr; -template inline -std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) -{ - using namespace srt_pair_op; - fmt::obufstream os; - os << "[ "; - for (auto i: in) - os << fmt::sfmt(i, fmt) << " "; - os << "]"; - return os.str(); -} - -// Separate version for pairs, used for std::map -template inline -std::string Printable(const Container& in, std::pair/*pseudoargument*/, const char* fmtk = 0, const char* fmtv = 0) -{ - using namespace srt_pair_op; - fmt::obufstream os; - os << "[ "; - for (auto i: in) - os << fmt::sfmt(i.first, fmtk) << ":" << fmt::sfmt(i.second, fmtv) << " "; - os << "]"; - return os.str(); -} - - -template inline -std::string Printable(const Container& in) -{ - using namespace srt_pair_op; - using Value = typename Container::value_type; - return Printable(in, Value()); -} - template auto map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) -> typename Map::mapped_type { @@ -719,20 +685,6 @@ inline std::string Sprint(const Arg1& arg1, const Arg2& arg2) return sout.str(); } -template inline -std::string Printable(const Container& in) -{ - using namespace srt_pair_op; - typedef typename Container::value_type Value; - fmt::obufstream os; - os << "[ "; - for (typename Container::const_iterator i = in.begin(); i != in.end(); ++i) - os << Value(*i) << " "; - os << "]"; - - return os.str(); -} - template typename Map::mapped_type map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) { @@ -763,6 +715,41 @@ typename Map::mapped_type const* map_getp(const Map& m, const Key& key) #endif +template inline +std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) +{ + fmt::obufstream os; + os << "[ "; + typedef typename Container::const_iterator it_t; + for (it_t i = in.begin(); i != in.end(); ++i) + os << fmt::sfmt(*i, fmt) << " "; + os << "]"; + return os.str(); +} + +// Separate version for pairs, used for std::map +template inline +std::string Printable(const Container& in, std::pair/*pseudoargument*/, const char* fmtk = 0, const char* fmtv = 0) +{ + using namespace srt_pair_op; + fmt::obufstream os; + os << "[ "; + typedef typename Container::const_iterator it_t; + for (it_t i = in.begin(); i != in.end(); ++i) + os << fmt::sfmt(i->first, fmtk) << ":" << fmt::sfmt(i->second, fmtv) << " "; + os << "]"; + return os.str(); +} + + +template inline +std::string Printable(const Container& in) +{ + using namespace srt_pair_op; + typedef typename Container::value_type Value; + return Printable(in, Value()); +} + // Printable with prefix added for every element. // Useful when printing a container of sockets or sequence numbers. template inline From 51997464083b02e3f77dec378aaa90fd23116e19 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 27 May 2024 23:10:09 +0200 Subject: [PATCH 133/517] Added extra version of snprintf for old Windows --- srtcore/sfmt.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 61bbd8914..cb64b90f5 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -617,18 +617,24 @@ class obufstream namespace internal { +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define FMT_SYM_SNPRINTF _snprintf +#define FMT_SIZE_SNPRINTF(bufsize) (bufsize-1) +#else +#define FMT_SYM_SNPRINTF std::snprintf +#define FMT_SIZE_SNPRINTF(bufsize) bufsize +#endif + template static inline size_t SNPrintfOne(char* buf, size_t bufsize, const char* fmt, const ValueType& val) -{ - return std::snprintf(buf, bufsize, fmt, val); -} - +{ return FMT_SYM_SNPRINTF (buf, FMT_SIZE_SNPRINTF(bufsize), fmt, val); } static inline size_t SNPrintfOne(char* buf, size_t bufsize, const char* fmt, const std::string& val) -{ - return std::snprintf(buf, bufsize, fmt, val.c_str()); -} +{ return FMT_SYM_SNPRINTF(buf, FMT_SIZE_SNPRINTF(bufsize), fmt, val.c_str()); } + } +#undef FMT_SYM_SNPRINTF +#undef FMT_SIZE_SNPRINTF template inline internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) From 4843143322967f65987ef16a3d97c6dc1346cd20 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 28 May 2024 09:27:37 +0200 Subject: [PATCH 134/517] Fixed the use of std::atomic --- srtcore/logging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/logging.h b/srtcore/logging.h index 0953a397d..94cda07be 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -366,7 +366,7 @@ struct LogDispatcher::Proxy // to the general version of operator<<, not the overload for // atomic. Even though the compiler shows Arg1 type as atomic. template - void dispatch(const std::atomic& a1, const Args&... others) + void dispatch(const srt::sync::atomic& a1, const Args&... others) { *this << a1.load(); dispatch(others...); From 1a0eca49e3d9dd9d92daf91720919c618d5342b0 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 28 May 2024 12:46:22 +0200 Subject: [PATCH 135/517] Fixed the right atomic type used with logging. Fixed some reported shadowed local variables --- testing/srt-test-relay.cpp | 6 +++--- testing/testactivemedia.cpp | 16 ++++++++++++++++ testing/testactivemedia.hpp | 20 ++------------------ testing/testmedia.cpp | 29 ++++++++++++++--------------- testing/testmedia.hpp | 5 +++-- testing/testmediabase.hpp | 4 ++-- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index 45d657128..18e6acf3d 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 tm { new TargetMedium }; + tm->Setup(m_srt_relay.get()); + m_output_media.push_back(move(tm)); } else { diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 9f2f03c98..44c96ee65 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -125,4 +125,20 @@ void TargetMedium::Runner() } } +bool TargetMedium::Schedule(const MediaPacket& data) +{ + LOGP(applog.Debug, "TargetMedium::Schedule 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) + { + LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); + return false; + } + + LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): Schedule: [", data.payload.size(), "] CLIENT -> BUFFER"); + buffer.push_back(data); + ready.notify_one(); + return true; +} diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index 011dcbfe7..e92abbe0c 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "testmedia.hpp" @@ -29,7 +28,7 @@ struct Medium std::mutex buffer_lock; std::thread thr; std::condition_variable ready; - std::atomic running = {false}; + srt::sync::atomic running {false}; std::exception_ptr xp; // To catch exception thrown by a thread virtual void Runner() = 0; @@ -147,22 +146,7 @@ struct TargetMedium: Medium { void Runner() override; - bool Schedule(const MediaPacket& data) - { - LOGP(applog.Debug, "TargetMedium::Schedule 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) - { - LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet"); - return false; - } - - LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): Schedule: [", data.payload.size(), "] CLIENT -> BUFFER"); - buffer.push_back(data); - ready.notify_one(); - return true; - } + bool Schedule(const MediaPacket& data); void Clear() { diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..197ee2231 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #if !defined(_WIN32) #include @@ -50,8 +49,8 @@ using srt_logging::SockStatusStr; using srt_logging::MemberStatusStr; #endif -std::atomic transmit_throw_on_interrupt {false}; -std::atomic transmit_int_state {false}; +srt::sync::atomic transmit_throw_on_interrupt {false}; +srt::sync::atomic transmit_int_state {false}; int transmit_bw_report = 0; unsigned transmit_stats_report = 0; size_t transmit_chunk_size = SRT_LIVE_DEF_PLSIZE; @@ -1059,8 +1058,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 ei = 1; ei < extras.size(); ++ei) + Verb() << "&" << extras[ei] << VerbNoEOL; } Verb(); @@ -1130,15 +1129,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 ti = 0; ti < targets.size(); ++ti) { // 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[ti].id != -1 && targets[ti].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[ti].socket = targets[ti].id; } } @@ -1159,12 +1158,12 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t ni = 0; ni < m_group_nodes.size(); ++ni) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[ni].socket; if (insock == -1) { - Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; + Verb() << "TARGET '" << sockaddr_any(targets[ni].peeraddr).str() << "' connection failed."; continue; } @@ -1194,11 +1193,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int ri = 0; ri < len1; ++ri) + Verb() << " " << ready_conn[ri] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int ri = 0; ri < len2; ++ri) + Verb() << " " << ready_err[ri] << VerbNoEOL; Verb() << ""; diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index be72471d1..470e825ef 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -15,7 +15,8 @@ #include #include #include -#include + +#include // use srt::sync::atomic instead of std::atomic for the sake of logging #include "apputil.hpp" #include "statswriter.hpp" @@ -25,7 +26,7 @@ extern srt_listen_callback_fn* transmit_accept_hook_fn; extern void* transmit_accept_hook_op; -extern std::atomic transmit_int_state; +extern srt::sync::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; diff --git a/testing/testmediabase.hpp b/testing/testmediabase.hpp index 04a85d435..686198787 100644 --- a/testing/testmediabase.hpp +++ b/testing/testmediabase.hpp @@ -11,17 +11,17 @@ #ifndef INC_SRT_COMMON_TRANMITBASE_HPP #define INC_SRT_COMMON_TRANMITBASE_HPP -#include #include #include #include #include #include +#include "sync.h" #include "uriparser.hpp" typedef std::vector bytevector; -extern std::atomic transmit_throw_on_interrupt; +extern srt::sync::atomic transmit_throw_on_interrupt; extern int transmit_bw_report; extern unsigned transmit_stats_report; extern size_t transmit_chunk_size; From 96437026900b32f06aab567cc6bb824938366885 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Tue, 28 May 2024 21:21:06 +0200 Subject: [PATCH 136/517] Fixed a clang-reported warning (to trigger rebuilding) --- testing/srt-test-live.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 17f4020dc..853ef87ad 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -301,7 +301,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb() << " type=" << gtypes[gt] << VerbNoEOL; else Verb() << " type=" << int(gt) << VerbNoEOL; From d9f079a3648acd219608784135201bf5e4d4494b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 3 Jun 2024 10:32:53 +0200 Subject: [PATCH 137/517] Turned integer-based values to strong types. CHECKPOINT: tests passed --- srtcore/buffer_rcv.cpp | 394 ++++++++++++++++++++++++----------------- srtcore/buffer_rcv.h | 215 ++++++++++++++++++---- srtcore/common.h | 2 + srtcore/core.cpp | 32 +--- srtcore/utilities.h | 20 ++- 5 files changed, 435 insertions(+), 228 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 6d6ebd841..ab72670df 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -78,18 +78,19 @@ namespace { // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. // The right edge is included because we expect iFirstNonreadPos to be // right after the last valid packet position if all packets are available. - bool isInRange(int iStartPos, int iMaxPosOff, size_t iSize, int iFirstNonreadPos) + bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) { if (iFirstNonreadPos == iStartPos) return true; - const int iLastPos = (iStartPos + iMaxPosOff) % iSize; - const bool isOverrun = iLastPos < iStartPos; + //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; + const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; if (isOverrun) - return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; } } @@ -120,14 +121,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(0) - , m_iEndPos(0) - , m_iDropPos(0) - , m_iFirstNonreadPos(0) + , m_iStartPos(&m_szSize, 0) + , m_iEndPos(&m_szSize, 0) + , m_iDropPos(&m_szSize, 0) + , m_iFirstNonreadPos(&m_szSize, 0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(-1) + , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -152,29 +153,34 @@ CRcvBuffer::~CRcvBuffer() void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { - HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos << " end=" << m_iEndPos - << " drop=" << m_iDropPos << " max-off=+" << m_iMaxPosOff << " seq[start]=%" << m_iStartSeqNo); + HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source + << ") start=" << m_iStartPos VALUE + << " end=" << m_iEndPos VALUE + << " drop=" << m_iDropPos VALUE + << " max-off=+" << m_iMaxPosOff VALUE + << " seq[start]=%" << m_iStartSeqNo VALUE); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); + const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::insert: seqno " << seqno); IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << unit->m_Packet.getMsgSeq(m_bPeerRexmitFlag)); IF_RCVBUF_DEBUG(scoped_log.ss << " m_iStartSeqNo " << m_iStartSeqNo << " offset " << offset); - if (offset < 0) + if (offset < COff(0)) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); return InsertInfo(InsertInfo::BELATED); } IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); - if (offset >= (int)capacity()) + if (offset >= COff(capacity())) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -3"); @@ -188,14 +194,14 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); - const int newpktpos = incPos(m_iStartPos, offset); - const int prev_max_off = m_iMaxPosOff; + const CPos newpktpos = m_iStartPos + offset; + const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) { - m_iMaxPosOff = offset + 1; + m_iMaxPosOff = offset + COff(1); extended_end = true; } @@ -204,7 +210,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); + SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -247,7 +253,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - int fallback_pos = -1; + CPos fallback_pos = CPos_TRAP; if (!m_tsbpd.isEnabled()) { // In case when TSBPD is off, we take into account the message mode @@ -271,22 +277,23 @@ void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); if (pkt) { - w_if.first_seq = pkt->getSeqNo(); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } } -const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) +const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) { if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = offPos(m_iStartPos, m_iEndPos); + w_span = m_iEndPos - m_iStartPos; } - if (pos == -1) + if (pos == CPos_TRAP) { - w_span = 0; + w_span = COff(0); return NULL; } @@ -297,15 +304,16 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(int pos, int& w_span) // be implemented for message mode, but still this would employ // a separate begin-end range declared for a complete out-of-order // message. - w_span = 1; + w_span = COff(1); return &packetAt(pos); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end) +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) { time_point earlier_time; - int prev_max_pos = incPos(m_iStartPos, prev_max_off); + //int prev_max_pos = incPos(m_iStartPos, prev_max_off); + CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -320,7 +328,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -328,7 +337,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -361,7 +371,9 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr // CONSIDER: make m_iDropPos rather m_iDropOff, this will make // this comparison a simple subtraction. Note that offset will // have to be updated on every shift of m_iStartPos. - else if (cmpPos(newpktpos, m_iDropPos) < 0) + + //else if (cmpPos(newpktpos, m_iDropPos) < 0) + else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -396,12 +408,12 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const int pr return earlier_time; } -void CRcvBuffer::updateGapInfo(int prev_max_pos) +void CRcvBuffer::updateGapInfo(CPos prev_max_pos) { - int pos = m_iEndPos; + CPos pos = m_iEndPos; // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status == EntryState_Empty) { @@ -420,7 +432,7 @@ void CRcvBuffer::updateGapInfo(int prev_max_pos) m_iEndPos = pos; m_iDropPos = pos; // fallback, although SHOULD be impossible // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; pos = incPos(pos)) + for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) { if (m_entries[pos].status != EntryState_Empty) { @@ -440,7 +452,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); - int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); + COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); + //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -451,25 +464,27 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; - while (len > 0) + const int iDropCnt = len VALUE; + while (len VALUE > 0) { dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - m_iStartPos = incPos(m_iStartPos); + //m_iStartPos = incPos(m_iStartPos); + ++m_iStartPos; --len; } // Update positions - m_iStartSeqNo = seqno; + m_iStartSeqNo = CSeqNo(seqno); // Move forward if there are "read/drop" entries. // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -490,7 +505,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); + const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -502,24 +518,28 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); + //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); + const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; + const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); return 0; } const bool bKeepExisting = (actionOnExisting == KEEP_EXISTING); - int minDroppedOffset = -1; + COff minDroppedOffset = COff(-1); int iDropCnt = 0; - const int start_off = max(0, offset_a); - const int start_pos = incPos(m_iStartPos, start_off); - const int end_off = min((int) m_szSize - 1, offset_b + 1); - const int end_pos = incPos(m_iStartPos, end_off); + const COff start_off = COff(max(0, offset_a)); + //const int start_pos = incPos(m_iStartPos, start_off); + const CPos start_pos = m_iStartPos + start_off; + const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); + //const int end_pos = incPos(m_iStartPos, end_off); + const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (int i = start_pos; i != end_pos; i = incPos(i)) + for (CPos i = start_pos; i != end_pos; ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -557,7 +577,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -567,8 +588,9 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - const int stop_pos = decPos(m_iStartPos); - for (int i = start_pos; i != stop_pos; i = decPos(i)) + //const int stop_pos = decPos(m_iStartPos); + const CPos stop_pos = m_iStartPos - COff(1); + for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -591,7 +613,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = offPos(m_iStartPos, i); + minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -619,7 +642,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (!m_tsbpd.isEnabled() && m_bMessageAPI) { if (!checkFirstReadableNonOrder()) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; updateFirstReadableNonOrder(); } @@ -632,16 +655,20 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iStartPos == m_iEndPos) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo); - w_seq = m_iStartSeqNo; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); + w_seq = m_iStartSeqNo VALUE; return m_iMaxPosOff > 0; } - int end_off = offPos(m_iStartPos, m_iEndPos); + //int end_off = offPos(m_iStartPos, m_iEndPos); + COff end_off = m_iEndPos - m_iStartPos; - w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + << " maxD=" << m_iMaxPosOff VALUE + << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); return (end_off < m_iMaxPosOff); @@ -650,13 +677,13 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) { const bool canReadInOrder = hasReadableInorderPkts(); - if (!canReadInOrder && m_iFirstNonOrderMsgPos < 0) + if (!canReadInOrder && m_iFirstNonOrderMsgPos == CPos_TRAP) { LOGC(rbuflog.Warn, log << "CRcvBuffer.readMessage(): nothing to read. Ignored isRcvDataReady() result?"); return 0; } - const int readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; + const CPos readPos = canReadInOrder ? m_iStartPos : m_iFirstNonOrderMsgPos; const bool isReadingFromStart = (readPos == m_iStartPos); // Indicates if the m_iStartPos can be changed IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -670,7 +697,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); - m_iStartSeqNo = CSeqNo::incseq(pktseqno); + m_iStartSeqNo = CSeqNo(pktseqno) + 1; } else { @@ -747,7 +774,7 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - p = incPos(p); + //p = incPos(p); + ++p; m_iNotch = 0; m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff >= 0); - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; } else m_iNotch += rs; @@ -923,7 +961,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -942,12 +981,13 @@ int CRcvBuffer::readBufferToFile(fstream& ofs, int len) bool CRcvBuffer::hasAvailablePackets() const { - return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return hasReadableInorderPkts() || (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos); + //return offPos(m_iStartPos, m_iFirstNonreadPos); + return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -958,7 +998,8 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -966,16 +1007,18 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - lastpos = decPos(lastpos); + //lastpos = decPos(lastpos); + --lastpos; } - + if (m_entries[lastpos].pUnit == NULL) return 0; - int startpos = m_iStartPos; + CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - startpos = incPos(startpos); + //startpos = incPos(startpos); + ++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1034,8 +1077,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - return std::pair(m_iStartSeqNo, seqno_last); + //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); + const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + return std::pair(m_iStartSeqNo VALUE, seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1047,7 +1092,7 @@ bool CRcvBuffer::isRcvDataReady(time_point time_now) const return true; SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != -1); + return (m_numNonOrderPackets > 0 && m_iFirstNonOrderMsgPos != CPos_TRAP); } if (!haveInorderPackets) @@ -1072,7 +1117,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstReadablePacketInfo(time_point time_no return info; } SRT_ASSERT((!m_bMessageAPI && m_numNonOrderPackets == 0) || m_bMessageAPI); - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) { SRT_ASSERT(m_numNonOrderPackets > 0); const CPacket& packet = packetAt(m_iFirstNonOrderMsgPos); @@ -1107,7 +1152,7 @@ void CRcvBuffer::countBytes(int pkts, int bytes) } } -void CRcvBuffer::releaseUnitInPos(int pos) +void CRcvBuffer::releaseUnitInPos(CPos pos) { CUnit* tmp = m_entries[pos].pUnit; m_entries[pos] = Entry(); // pUnit = NULL; status = Empty @@ -1115,7 +1160,7 @@ void CRcvBuffer::releaseUnitInPos(int pos) m_pUnitQueue->makeUnitFree(tmp); } -bool CRcvBuffer::dropUnitInPos(int pos) +bool CRcvBuffer::dropUnitInPos(CPos pos) { if (!m_entries[pos].pUnit) return false; @@ -1127,7 +1172,7 @@ bool CRcvBuffer::dropUnitInPos(int pos) { --m_numNonOrderPackets; if (pos == m_iFirstNonOrderMsgPos) - m_iFirstNonOrderMsgPos = -1; + m_iFirstNonOrderMsgPos = CPos_TRAP; } releaseUnitInPos(pos); return true; @@ -1135,12 +1180,14 @@ bool CRcvBuffer::dropUnitInPos(int pos) void CRcvBuffer::releaseNextFillerEntries() { - int pos = m_iStartPos; + CPos pos = m_iStartPos; while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { - m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); + ++m_iStartSeqNo; releaseUnitInPos(pos); - pos = incPos(pos); + //pos = incPos(pos); + ++pos; m_iStartPos = pos; --m_iMaxPosOff; if (m_iMaxPosOff < 0) @@ -1154,15 +1201,16 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. - int pos = m_iFirstNonreadPos; + CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) { if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (int i = pos; i != end_pos; i = incPos(i)) + for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1176,7 +1224,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = incPos(i); + m_iFirstNonreadPos = i + COff(1); break; } } @@ -1188,9 +1237,9 @@ void CRcvBuffer::updateNonreadPos() } } -int CRcvBuffer::findLastMessagePkt() +CPos CRcvBuffer::findLastMessagePkt() { - for (int i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1200,10 +1249,10 @@ int CRcvBuffer::findLastMessagePkt() } } - return -1; + return CPos_TRAP; } -void CRcvBuffer::onInsertNonOrderPacket(int insertPos) +void CRcvBuffer::onInsertNonOrderPacket(CPos insertPos) { if (m_numNonOrderPackets == 0) return; @@ -1214,7 +1263,7 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) // // There might happen that the packet being added precedes the previously found one. // However, it is allowed to re bead out of order, so no need to update the position. - if (m_iFirstNonOrderMsgPos >= 0) + if (m_iFirstNonOrderMsgPos != CPos_TRAP) return; // Just a sanity check. This function is called when a new packet is added. @@ -1233,14 +1282,14 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) const int msgNo = pkt.getMsgSeq(m_bPeerRexmitFlag); // First check last packet, because it is expected to be received last. - const bool hasLast = (boundary & PB_LAST) || (-1 < scanNonOrderMessageRight(insertPos, msgNo)); + const bool hasLast = (boundary & PB_LAST) || (scanNonOrderMessageRight(insertPos, msgNo) != CPos_TRAP); if (!hasLast) return; - const int firstPktPos = (boundary & PB_FIRST) + const CPos firstPktPos = (boundary & PB_FIRST) ? insertPos : scanNonOrderMessageLeft(insertPos, msgNo); - if (firstPktPos < 0) + if (firstPktPos == CPos_TRAP) return; m_iFirstNonOrderMsgPos = firstPktPos; @@ -1249,12 +1298,13 @@ void CRcvBuffer::onInsertNonOrderPacket(int insertPos) bool CRcvBuffer::checkFirstReadableNonOrder() { - if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos < 0 || m_iMaxPosOff == 0) + if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); + const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (int pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) { if (!m_entries[pos].pUnit) return false; @@ -1277,7 +1327,7 @@ bool CRcvBuffer::checkFirstReadableNonOrder() void CRcvBuffer::updateFirstReadableNonOrder() { - if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos >= 0) + if (hasReadableInorderPkts() || m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos != CPos_TRAP) return; if (m_iMaxPosOff == 0) @@ -1288,17 +1338,19 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); - int posFirst = -1; - int posLast = -1; + CPos posFirst = CPos_TRAP; + CPos posLast = CPos_TRAP; int msgNo = -1; - for (int pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) { if (!m_entries[pos].pUnit) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1306,7 +1358,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgOrderFlag()) // Skip in order packet { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1321,7 +1374,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { - posFirst = posLast = msgNo = -1; + posFirst = posLast = CPos_TRAP; + msgNo = -1; continue; } @@ -1338,18 +1392,20 @@ void CRcvBuffer::updateFirstReadableNonOrder() return; } -int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const { // Search further packets to the right. // First check if there are packets to the right. - const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; + const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (startPos == lastPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = incPos(pos); + //pos = incPos(pos); + ++pos; if (!m_entries[pos].pUnit) break; @@ -1358,7 +1414,7 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_LAST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1366,30 +1422,31 @@ int CRcvBuffer::scanNonOrderMessageRight(const int startPos, int msgNo) const return pos; } while (pos != lastPos); - return -1; + return CPos_TRAP; } -int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const +CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const { // Search preceding packets to the left. // First check if there are packets to the left. if (startPos == m_iStartPos) - return -1; + return CPos_TRAP; - int pos = startPos; + CPos pos = startPos; do { - pos = decPos(pos); + //pos = decPos(pos); + --pos; if (!m_entries[pos].pUnit) - return -1; + return CPos_TRAP; const CPacket& pkt = packetAt(pos); if (pkt.getMsgSeq(m_bPeerRexmitFlag) != msgNo) { LOGC(rbuflog.Error, log << "Missing PB_FIRST packet for msgNo " << msgNo); - return -1; + return CPos_TRAP; } const PacketBoundary boundary = pkt.getMsgBoundary(); @@ -1397,7 +1454,7 @@ int CRcvBuffer::scanNonOrderMessageLeft(const int startPos, int msgNo) const return pos; } while (pos != m_iStartPos); - return -1; + return CPos_TRAP; } bool CRcvBuffer::addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample) @@ -1435,12 +1492,12 @@ void CRcvBuffer::updateTsbPdTimeBase(uint32_t usPktTimestamp) m_tsbpd.updateTsbPdTimeBase(usPktTimestamp); } -string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const +string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo - << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1451,7 +1508,7 @@ string CRcvBuffer::strFullnessState(int iFirstUnackSeqNo, const time_point& tsNo if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const int iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1502,35 +1559,39 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { - int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); + int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; + COff offset (offset_val); // Check if it's still inside the buffer - if (offset < 0 || offset >= m_iMaxPosOff) + if (offset_val < 0 || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq - << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq + << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } // Start position - int frompos = incPos(m_iStartPos, offset); + //int frompos = incPos(m_iStartPos, offset); + CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then // m_iEnd stands on the first loss, unless it's equal // to the position pointed by m_iMaxPosOff. - int32_t ret_seq = SRT_SEQNO_NONE; - int ret_off = m_iMaxPosOff; + CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); + COff ret_off = m_iMaxPosOff; - int end_off = offPos(m_iStartPos, m_iEndPos); - if (frompos < end_off) + COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } } @@ -1544,11 +1605,13 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - int pos = incPos(m_iStartPos, offset); + //int pos = incPos(m_iStartPos, offset); + const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; - ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); + ret_seq = m_iStartSeqNo + offset VALUE; break; } } @@ -1559,26 +1622,31 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Also no need to search anything if only the beginning was // being looked for. - if (ret_seq == SRT_SEQNO_NONE || !pw_end) - return ret_seq; + if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) + return ret_seq VALUE; // We want also the end range, so continue from where you // stopped. // Start from ret_off + 1 because we know already that ret_off // points to an empty cell. - for (int off = ret_off + 1; off < m_iMaxPosOff; ++off) + for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - int pos = incPos(m_iStartPos, off); + //int pos = incPos(m_iStartPos, off); + const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); - return ret_seq; + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; + return ret_seq VALUE; } } // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos << " %" << CSeqNo::incseq(m_iStartSeqNo, ret_off) << " not followed by any valid cell"); + LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" + //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) + << (m_iStartSeqNo + ret_off) VALUE + << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when // a packet has somehow disappeared, but it contains empty cells up to the diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 24f5066f6..18450947d 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -15,10 +15,162 @@ #include "common.h" #include "queue.h" #include "tsbpd_time.h" +#include "utilities.h" + +#define USE_WRAPPERS 1 +#define USE_OPERATORS 1 namespace srt { +// DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) + +#if USE_WRAPPERS +struct CPos +{ + int value; + const size_t* psize; + + int isize() const {return *psize;} + + explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + int val() const { return value; } + explicit operator int() const {return value;} + + CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos& operator=(const CPos& src) + { + psize = src.psize; + value = src.value; + return *this; + } + + int cmp(CPos other, CPos start) const + { + int pos2 = value; + int pos1 = other.value; + + const int off1 = pos1 >= start.value ? pos1 - start.value : pos1 + start.isize() - start.value; + const int off2 = pos2 >= start.value ? pos2 - start.value : pos2 + start.isize() - start.value; + + return off2 - off1; + } + + CPos& operator--() + { + if (value == 0) + value = isize() - 1; + else + --value; + return *this; + } + + CPos& operator++() + { + ++value; + if (value == isize()) + value = 0; + return *this; + } + + bool operator == (CPos other) const { return value == other.value; } + bool operator != (CPos other) const { return value != other.value; } +}; + +struct COff +{ + int value; + explicit COff(int v): value(v) {} + COff& operator=(int v) { value = v; return *this; } + + int val() const { return value; } + explicit operator int() const {return value;} + + COff& operator--() { --value; return *this; } + COff& operator++() { ++value; return *this; } + + COff operator--(int) { int v = value; --value; return COff(v); } + COff operator++(int) { int v = value; ++value; return COff(v); } + + COff operator+(COff other) const { return COff(value + other.value); } + COff operator-(COff other) const { return COff(value - other.value); } + COff& operator+=(COff other) { value += other.value; return *this;} + COff& operator-=(COff other) { value -= other.value; return *this;} + + bool operator == (COff other) const { return value == other.value; } + bool operator != (COff other) const { return value != other.value; } + bool operator < (COff other) const { return value < other.value; } + bool operator > (COff other) const { return value > other.value; } + bool operator <= (COff other) const { return value <= other.value; } + bool operator >= (COff other) const { return value >= other.value; } + + // Exceptionally allow modifications of COff by a bare integer + COff operator+(int other) const { return COff(value + other); } + COff operator-(int other) const { return COff(value - other); } + COff& operator+=(int other) { value += other; return *this;} + COff& operator-=(int other) { value -= other; return *this;} + + bool operator == (int other) const { return value == other; } + bool operator != (int other) const { return value != other; } + bool operator < (int other) const { return value < other; } + bool operator > (int other) const { return value > other; } + bool operator <= (int other) const { return value <= other; } + bool operator >= (int other) const { return value >= other; } +}; + +#define VALUE .val() + +#if USE_OPERATORS + +inline CPos operator+(const CPos& pos, COff off) +{ + int val = pos.value + off.value; + while (val >= pos.isize()) + val -= pos.isize(); + return CPos(pos.psize, val); +} + +inline CPos operator-(const CPos& pos, COff off) +{ + int val = pos.value - off.value; + while (val < 0) + val += pos.isize(); + return CPos(pos.psize, val); +} + +// Should verify that CPos use the same size! +inline COff operator-(CPos later, CPos earlier) +{ + if (later.value < earlier.value) + return COff(later.value + later.isize() - earlier.value); + + return COff(later.value - earlier.value); +} + +inline CSeqNo operator+(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::incseq(seq.val(), off.val()); + return CSeqNo(val); +} + +inline CSeqNo operator-(CSeqNo seq, COff off) +{ + int32_t val = CSeqNo::decseq(seq.val(), off.val()); + return CSeqNo(val); +} + + +#endif +const size_t fix_rollover = 16; +const CPos CPos_TRAP (&fix_rollover, -1); + +#else +#define VALUE +typedef int CPos; +typedef int COff; +const int CPos_TRAP = -1; +#endif + // // Circular receiver buffer. // @@ -229,9 +381,9 @@ class CRcvBuffer // Below fields are valid only if result == INSERTED. Otherwise they have trap repro. - int first_seq; // sequence of the first available readable packet + CSeqNo first_seq; // sequence of the first available readable packet time_point first_time; // Time of the new, earlier packet that appeared ready, or null-time if this didn't change. - int avail_range; + COff avail_range; InsertInfo(Result r, int fp_seq = SRT_SEQNO_NONE, int range = 0, time_point fp_time = time_point()) @@ -265,8 +417,8 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const int prev_max_off, const int newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(int pos, int& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); + const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -289,7 +441,7 @@ class CRcvBuffer /// /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` /// - void updateGapInfo(int prev_max_pos); + void updateGapInfo(CPos prev_max_pos); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -362,31 +514,32 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo; } + int getStartSeqNo() const { return m_iStartSeqNo VALUE; } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; } + void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. /// Effective returns capacity of the buffer minus acknowledged packet still kept in it. // TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer. - size_t getAvailSize(int iFirstUnackSeqNo) const + size_t getAvailSize(int32_t iFirstUnackSeqNo) const { // Receiver buffer allows reading unacknowledged packets. // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const int iRBufSeqNo = getStartSeqNo(); - if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + const CSeqNo iRBufSeqNo = m_iStartSeqNo; + //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. @@ -436,7 +589,7 @@ class CRcvBuffer bool empty() const { - return (m_iMaxPosOff == 0); + return (m_iMaxPosOff == COff(0)); } /// Return buffer capacity. @@ -453,7 +606,7 @@ class CRcvBuffer /// between the initial position and the youngest received packet. size_t size() const { - return m_iMaxPosOff; + return size_t(m_iMaxPosOff VALUE); } // Returns true if the buffer is full. Requires locking. @@ -489,6 +642,7 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: + /* inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } @@ -506,20 +660,21 @@ class CRcvBuffer return off2 - off1; } + */ // NOTE: Assumes that pUnit != NULL - CPacket& packetAt(int pos) { return m_entries[pos].pUnit->m_Packet; } - const CPacket& packetAt(int pos) const { return m_entries[pos].pUnit->m_Packet; } + CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } + const CPacket& packetAt(CPos pos) const { return m_entries[pos].pUnit->m_Packet; } private: void countBytes(int pkts, int bytes); void updateNonreadPos(); - void releaseUnitInPos(int pos); + void releaseUnitInPos(CPos pos); /// @brief Drop a unit from the buffer. /// @param pos position in the m_entries of the unit to drop. /// @return false if nothing to drop, true if the unit was dropped successfully. - bool dropUnitInPos(int pos); + bool dropUnitInPos(CPos pos); /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). @@ -528,15 +683,15 @@ class CRcvBuffer bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } /// Find position of the last packet of the message. - int findLastMessagePkt(); + CPos findLastMessagePkt(); /// Scan for availability of out of order packets. - void onInsertNonOrderPacket(int insertpos); + void onInsertNonOrderPacket(CPos insertpos); // Check if m_iFirstNonOrderMsgPos is still readable. bool checkFirstReadableNonOrder(); void updateFirstReadableNonOrder(); - int scanNonOrderMessageRight(int startPos, int msgNo) const; - int scanNonOrderMessageLeft(int startPos, int msgNo) const; + CPos scanNonOrderMessageRight(CPos startPos, int msgNo) const; + CPos scanNonOrderMessageLeft(CPos startPos, int msgNo) const; typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg); @@ -588,18 +743,18 @@ class CRcvBuffer //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - typedef FixedArray entries_t; + typedef FixedArray entries_t; entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue - int m_iStartSeqNo; - int m_iStartPos; // the head position for I/O (inclusive) - int m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - int m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet - int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) - int m_iMaxPosOff; // the furthest data position + CSeqNo m_iStartSeqNo; + CPos m_iStartPos; // the head position for I/O (inclusive) + CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos + CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) + COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) size_t m_numNonOrderPackets; // The number of stored packets with "inorder" flag set to false @@ -607,7 +762,7 @@ class CRcvBuffer /// Points to the first packet of a message that has out-of-order flag /// and is complete (all packets from first to last are in the buffer). /// If there is no such message in the buffer, it contains -1. - int m_iFirstNonOrderMsgPos; + CPos m_iFirstNonOrderMsgPos; bool m_bPeerRexmitFlag; // Needed to read message number correctly const bool m_bMessageAPI; // Operation mode flag: message or stream. @@ -637,7 +792,7 @@ class CRcvBuffer /// Form a string of the current buffer fullness state. /// number of packets acknowledged, TSBPD readiness, etc. - std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const; + std::string strFullnessState(int32_t iFirstUnackSeqNo, const time_point& tsNow) const; private: CTsbpdTime m_tsbpd; diff --git a/srtcore/common.h b/srtcore/common.h index 6a8912118..1c15dd01a 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -580,6 +580,8 @@ class CSeqNo explicit CSeqNo(int32_t v): value(v) {} + int32_t val() const { return value; } + // Comparison bool operator == (const CSeqNo& other) const { return other.value == value; } bool operator < (const CSeqNo& other) const diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 45aeb2b4c..ad42ceb92 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -8192,24 +8192,12 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) log << CONID() << "ACK: clip %" << m_iRcvLastAck << "-%" << ack << ", REVOKED " << CSeqNo::seqoff(ack, m_iRcvLastAck) << " from RCV buffer"); - if (m_bTsbPd) - { - /* - There's no need to update TSBPD in the wake-on-recv state - from ACK because it is being done already in the receiver thread - when a newly inserted packet caused provision of a new candidate - that could be delivered soon. Also, this flag is only used in TSBPD - mode and can be only set to true in the TSBPD thread. - - // Newly acknowledged data, signal TsbPD thread // - CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); - // m_bWakeOnRecv is protected by m_RecvLock in the tsbpd() thread - if (m_bWakeOnRecv) - tslcc.notify_one(); - - */ - } - else + // There's no need to update TSBPD in the wake-on-recv state + // from ACK because it is being done already in the receiver thread + // when a newly inserted packet caused provision of a new candidate + // that could be delivered soon. Also, this flag is only used in TSBPD + // mode and can be only set to true in the TSBPD thread. + if (!m_bTsbPd) { { CUniqueSync rdcc (m_RecvLock, m_RecvDataCond); @@ -10682,14 +10670,6 @@ int srt::CUDT::processData(CUnit* in_unit) { HLOGC(qrlog.Debug, log << CONID() << "WILL REPORT LOSSES (filter): " << Printable(filter_loss_seqs)); sendLossReport(filter_loss_seqs); - - // XXX unsure as to whether this should change anything in the TSBPD conditions. - // Might be that this trigger is not necessary. - if (m_bTsbPd) - { - HLOGC(qrlog.Debug, log << CONID() << "loss: signaling TSBPD cond"); - CSync::lock_notify_one(m_RcvTsbPdCond, m_RecvLock); - } } // Now review the list of FreshLoss to see if there's any "old enough" to send UMSG_LOSSREPORT to it. diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 8a1374eb7..9dbebe762 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -414,7 +414,7 @@ struct DynamicStruct /// Fixed-size array template class. namespace srt { -template +template class FixedArray { public: @@ -430,22 +430,23 @@ class FixedArray } public: - const T& operator[](size_t index) const + const T& operator[](Indexer index) const { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } - T& operator[](size_t index) + T& operator[](Indexer index) { - if (index >= m_size) - throw_invalid_index(index); + if (int(index) >= int(m_size)) + throw_invalid_index(int(index)); - return m_entries[index]; + return m_entries[int(index)]; } + /* const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) @@ -461,6 +462,7 @@ class FixedArray return m_entries[index]; } + */ size_t size() const { return m_size; } From ab469d65d58a9e4ec825d4557bc405f70e8abb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 10 Jun 2024 11:09:06 +0200 Subject: [PATCH 138/517] Fixed previous problems with receiver buffer --- srtcore/buffer_rcv.cpp | 168 +++++++++++++++++++---------------------- srtcore/buffer_rcv.h | 61 ++++++++++++--- 2 files changed, 129 insertions(+), 100 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index ab72670df..539c51f9e 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -75,23 +75,6 @@ namespace { #define IF_RCVBUF_DEBUG(instr) (void)0 - // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. - // The right edge is included because we expect iFirstNonreadPos to be - // right after the last valid packet position if all packets are available. - bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) - { - if (iFirstNonreadPos == iStartPos) - return true; - - //const int iLastPos = (iStartPos VALUE + iMaxPosOff VALUE) % iSize; - const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; - - if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; - - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; - } } @@ -194,9 +177,10 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // TODO: Don't do assert here. Process this situation somehow. // If >= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos + offset) VALUE / m_szSize < 2); + SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); - const CPos newpktpos = m_iStartPos + offset; + //const CPos newpktpos = m_iStartPos + offset; + const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; if (offset >= m_iMaxPosOff) @@ -287,8 +271,8 @@ const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) if (m_entries[m_iStartPos].status == EntryState_Avail) { pos = m_iStartPos; - //w_span = offPos(m_iStartPos, m_iEndPos); - w_span = m_iEndPos - m_iStartPos; + w_span = offPos(m_iStartPos, m_iEndPos); + //w_span = m_iEndPos - m_iStartPos; } if (pos == CPos_TRAP) @@ -312,8 +296,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p { time_point earlier_time; - //int prev_max_pos = incPos(m_iStartPos, prev_max_off); - CPos prev_max_pos = m_iStartPos + prev_max_off; + CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); + //CPos prev_max_pos = m_iStartPos + prev_max_off; // Update flags // Case [A] @@ -328,8 +312,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // This means that m_iEndPos now shifts by 1, // and m_iDropPos must be shifted together with it, // as there's no drop to point. - //m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - m_iEndPos = m_iStartPos + m_iMaxPosOff; + m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); + //m_iEndPos = m_iStartPos + m_iMaxPosOff; m_iDropPos = m_iEndPos; } else @@ -337,8 +321,8 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. // Therefore m_iEndPos STAYS WHERE IT IS. - //m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); } } } @@ -483,12 +467,12 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // Start from here and search fort the next gap m_iEndPos = m_iDropPos = m_iStartPos; - //updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); + //updateGapInfo(m_iStartPos + m_iMaxPosOff); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); @@ -533,13 +517,13 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro COff minDroppedOffset = COff(-1); int iDropCnt = 0; const COff start_off = COff(max(0, offset_a)); - //const int start_pos = incPos(m_iStartPos, start_off); - const CPos start_pos = m_iStartPos + start_off; + const CPos start_pos = incPos(m_iStartPos, start_off); + //const CPos start_pos = m_iStartPos + start_off; const COff end_off = COff(min((int) m_szSize - 1, offset_b + 1)); - //const int end_pos = incPos(m_iStartPos, end_off); - const CPos end_pos = m_iStartPos + end_off; + const CPos end_pos = incPos(m_iStartPos, end_off); + //const CPos end_pos = m_iStartPos + end_off; bool bDropByMsgNo = msgno > SRT_MSGNO_CONTROL; // Excluding both SRT_MSGNO_NONE (-1) and SRT_MSGNO_CONTROL (0). - for (CPos i = start_pos; i != end_pos; ++i) + for (CPos i = start_pos; i != end_pos; i = incPos(i)) // ++i) { // Check if the unit was already dropped earlier. if (m_entries[i].status == EntryState_Drop) @@ -577,8 +561,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro ++iDropCnt; m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; } if (bDropByMsgNo) @@ -588,8 +572,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // The sender should have the last packet of the message it is requesting to be dropped. // Therefore we don't search forward, but need to check earlier packets in the RCV buffer. // Try to drop by the message number in case the message starts earlier than @a seqnolo. - //const int stop_pos = decPos(m_iStartPos); - const CPos stop_pos = m_iStartPos - COff(1); + const CPos stop_pos = decPos(m_iStartPos); + //const CPos stop_pos = m_iStartPos - COff(1); for (CPos i = start_pos; i != stop_pos; --i) { // Can't drop if message number is not known. @@ -613,8 +597,8 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro dropUnitInPos(i); m_entries[i].status = EntryState_Drop; // As the search goes backward, i is always earlier than minDroppedOffset. - //minDroppedOffset = offPos(m_iStartPos, i); - minDroppedOffset = i - m_iStartPos; + minDroppedOffset = offPos(m_iStartPos, i); + //minDroppedOffset = i - m_iStartPos; // Break the loop if the start of the message has been found. No need to search further. if (bnd == PB_FIRST) @@ -660,8 +644,8 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - //int end_off = offPos(m_iStartPos, m_iEndPos); - COff end_off = m_iEndPos - m_iStartPos; + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; @@ -745,7 +729,8 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= remain_pktlen) { releaseUnitInPos(p); - //p = incPos(p); - ++p; + p = incPos(p); + //++p; m_iNotch = 0; m_iStartPos = p; @@ -954,7 +939,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) // Update positions // Set nonread position to the starting position before updating, // because start position was increased, and preceding packets are invalid. - if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; } @@ -986,8 +971,8 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - //return offPos(m_iStartPos, m_iFirstNonreadPos); - return (m_iFirstNonreadPos - m_iStartPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + //return (m_iFirstNonreadPos - m_iStartPos) VALUE; } int CRcvBuffer::getTimespan_ms() const @@ -998,8 +983,9 @@ int CRcvBuffer::getTimespan_ms() const if (m_iMaxPosOff == 0) return 0; - //int lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); - CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + CPos lastpos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //CPos lastpos = m_iStartPos + (m_iMaxPosOff - COff(1)); + // Normally the last position should always be non empty // if TSBPD is enabled (reading out of order is not allowed). // However if decryption of the last packet fails, it may be dropped @@ -1007,8 +993,8 @@ int CRcvBuffer::getTimespan_ms() const SRT_ASSERT(m_entries[lastpos].pUnit != NULL || m_entries[lastpos].status == EntryState_Drop); while (m_entries[lastpos].pUnit == NULL && lastpos != m_iStartPos) { - //lastpos = decPos(lastpos); - --lastpos; + lastpos = decPos(lastpos); + //--lastpos; } if (m_entries[lastpos].pUnit == NULL) @@ -1017,8 +1003,8 @@ int CRcvBuffer::getTimespan_ms() const CPos startpos = m_iStartPos; while (m_entries[startpos].pUnit == NULL && startpos != lastpos) { - //startpos = incPos(startpos); - ++startpos; + startpos = incPos(startpos); + //++startpos; } if (m_entries[startpos].pUnit == NULL) @@ -1077,9 +1063,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { - //const int seqno_last = CSeqNo::incseq(m_iStartSeqNo, offPos(m_iStartPos, m_iFirstNonreadPos)); - const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; - const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; + const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; + //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; return std::pair(m_iStartSeqNo VALUE, seqno_last); } @@ -1201,8 +1188,8 @@ void CRcvBuffer::updateNonreadPos() if (m_iMaxPosOff == 0) return; - //const int end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. - const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. + const CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); // The empty position right after the last valid entry. + //const CPos end_pos = m_iStartPos + m_iMaxPosOff; // The empty position right after the last valid entry. CPos pos = m_iFirstNonreadPos; while (m_entries[pos].pUnit && m_entries[pos].status == EntryState_Avail) @@ -1224,8 +1211,8 @@ void CRcvBuffer::updateNonreadPos() // Check PB_LAST only in message mode. if (!m_bMessageAPI || packetAt(i).getMsgBoundary() & PB_LAST) { - //m_iFirstNonreadPos = incPos(i); - m_iFirstNonreadPos = i + COff(1); + m_iFirstNonreadPos = incPos(i); + //m_iFirstNonreadPos = i + COff(1); break; } } @@ -1301,10 +1288,10 @@ bool CRcvBuffer::checkFirstReadableNonOrder() if (m_numNonOrderPackets <= 0 || m_iFirstNonOrderMsgPos == CPos_TRAP || m_iMaxPosOff == COff(0)) return false; - //const int endPos = incPos(m_iStartPos, m_iMaxPosOff); - const CPos endPos = m_iStartPos + m_iMaxPosOff; + const CPos endPos = incPos(m_iStartPos, m_iMaxPosOff); + //const CPos endPos = m_iStartPos + m_iMaxPosOff; int msgno = -1; - for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; ++pos) // pos = incPos(pos)) + for (CPos pos = m_iFirstNonOrderMsgPos; pos != endPos; pos = incPos(pos)) // ++pos) { if (!m_entries[pos].pUnit) return false; @@ -1339,7 +1326,8 @@ void CRcvBuffer::updateFirstReadableNonOrder() // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); CPos posFirst = CPos_TRAP; CPos posLast = CPos_TRAP; @@ -1397,7 +1385,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const // Search further packets to the right. // First check if there are packets to the right. //const int lastPos = (m_iStartPos + m_iMaxPosOff - 1) % m_szSize; - const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + //const CPos lastPos = m_iStartPos + m_iMaxPosOff - COff(1); + const CPos lastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); if (startPos == lastPos) return CPos_TRAP; @@ -1508,7 +1497,8 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& if (!is_zero(nextValidPkt.tsbpd_time)) { ss << count_milliseconds(nextValidPkt.tsbpd_time - tsNow) << "ms"; - const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); //incPos(m_iStartPos, m_iMaxPosOff - 1); + const CPos iLastPos = incPos(m_iStartPos, m_iMaxPosOff - 1); + //const CPos iLastPos = m_iStartPos + m_iMaxPosOff - COff(1); if (m_entries[iLastPos].pUnit) { ss << ", timespan "; @@ -1572,8 +1562,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) } // Start position - //int frompos = incPos(m_iStartPos, offset); - CPos frompos = m_iStartPos + offset; + CPos frompos = incPos(m_iStartPos, offset); + //CPos frompos = m_iStartPos + offset; // Ok; likely we should stand at the m_iEndPos position. // If this given position is earlier than this, then @@ -1582,15 +1572,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); COff ret_off = m_iMaxPosOff; - - COff end_off = m_iEndPos - m_iStartPos; //offPos(m_iStartPos, m_iEndPos); + COff end_off = offPos(m_iStartPos, m_iEndPos); + //COff end_off = m_iEndPos - m_iStartPos; if (offset < end_off) { // If m_iEndPos has such a value, then there are // no loss packets at all. if (end_off != m_iMaxPosOff) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); + //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); ret_seq = m_iStartSeqNo + end_off VALUE; ret_off = end_off; } @@ -1605,8 +1595,8 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // REUSE offset as a control variable for (; offset < m_iMaxPosOff; ++offset) { - //int pos = incPos(m_iStartPos, offset); - const CPos pos = m_iStartPos + offset; + const CPos pos = incPos(m_iStartPos, offset); + //const CPos pos = m_iStartPos + offset; if (m_entries[pos].status == EntryState_Empty) { ret_off = offset; @@ -1632,11 +1622,11 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // points to an empty cell. for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) { - //int pos = incPos(m_iStartPos, off); - const CPos pos = m_iStartPos + off; + const CPos pos = incPos(m_iStartPos, off); + //const CPos pos = m_iStartPos + off; if (m_entries[pos].status != EntryState_Empty) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, off - 1); + //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; return ret_seq VALUE; } @@ -1645,7 +1635,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // Fallback - this should be impossible, so issue a log. LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off) VALUE + << (m_iStartSeqNo + ret_off VALUE) VALUE << " not followed by any valid cell"); // Return this in the last resort - this could only be a situation when diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 18450947d..ff659a45c 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -18,7 +18,7 @@ #include "utilities.h" #define USE_WRAPPERS 1 -#define USE_OPERATORS 1 +#define USE_OPERATORS 0 namespace srt { @@ -33,7 +33,7 @@ struct CPos int isize() const {return *psize;} - explicit CPos(explicit_t ps, explicit_t val): value(val), psize(ps) {} + explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} @@ -642,25 +642,64 @@ class CRcvBuffer const CUnit* peek(int32_t seqno); private: - /* - inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } - inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } - inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + //* + inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } + inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } + inline COff offPos(CPos pos1, CPos pos2) const + { + int diff = pos2 VALUE - pos1 VALUE; + if (diff >= 0) + { + return COff(diff); + } + return COff(m_szSize + diff); + } + + inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. /// @param pos1 a position in the receiver buffer. /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. - inline int cmpPos(int pos2, int pos1) const + inline COff cmpPos(CPos pos2, CPos pos1) const { // XXX maybe not the best implementation, but this keeps up to the rule. // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. - const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; - const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; - return off2 - off1; + return posToOff(pos2) - posToOff(pos1); + } + // */ + + // Check if iFirstNonreadPos is in range [iStartPos, (iStartPos + iMaxPosOff) % iSize]. + // The right edge is included because we expect iFirstNonreadPos to be + // right after the last valid packet position if all packets are available. + static bool isInRange(CPos iStartPos, COff iMaxPosOff, size_t iSize, CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == iStartPos) + return true; + + const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + //const CPos iLastPos = iStartPos + iMaxPosOff; + const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + + if (isOverrun) + return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + + return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + } + + bool isInUsedRange(CPos iFirstNonreadPos) + { + if (iFirstNonreadPos == m_iStartPos) + return true; + + // DECODE the iFirstNonreadPos + int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + if (diff < 0) + diff += m_szSize; + + return diff <= m_iMaxPosOff VALUE; } - */ // NOTE: Assumes that pUnit != NULL CPacket& packetAt(CPos pos) { return m_entries[pos].pUnit->m_Packet; } From f4ecbc19349f9a7e3580f164019d41345ed19212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 17 Jun 2024 11:39:11 +0200 Subject: [PATCH 139/517] Updated sfmt.h, fixed sync formatting --- srtcore/sfmt.h | 383 +++++++++++++++++++++++++++++++++++++++++++---- srtcore/sync.cpp | 14 +- 2 files changed, 356 insertions(+), 41 deletions(-) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 61bbd8914..7b10e03e9 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -331,36 +332,325 @@ inline form_memory_buffer<> apply_format_fix(TYPE, const char* fmt)\ // be used. They will be inserted if needed. SFMT_FORMAT_FIXER(int, "+- '#", "dioxX", "i", ""); -// Short is simple because it's aligned to int anyway SFMT_FORMAT_FIXER(short int, "+- '#", "dioxX", "hi", ""); - SFMT_FORMAT_FIXER(long int, "+- '#", "dioxX", "li", ""); - SFMT_FORMAT_FIXER(long long int, "+- '#", "dioxX", "lli", ""); SFMT_FORMAT_FIXER(unsigned int, "+- '#", "uoxX", "u", ""); - SFMT_FORMAT_FIXER(unsigned short int, "+- '#", "uoxX", "hu", ""); - SFMT_FORMAT_FIXER(unsigned long int, "+- '#", "uoxX", "lu", ""); - SFMT_FORMAT_FIXER(unsigned long long int, "+- '#", "uoxX", "llu", ""); SFMT_FORMAT_FIXER(double, "+- '#.", "EeFfgGaA", "g", ""); SFMT_FORMAT_FIXER(float, "+- '#.", "EeFfgGaA", "g", ""); SFMT_FORMAT_FIXER(long double, "+- '#.", "EeFfgGaA", "Lg", ""); -SFMT_FORMAT_FIXER(char, "", "c", "c", ""); -SFMT_FORMAT_FIXER(std::string, "", "s", "s", ""); -SFMT_FORMAT_FIXER(const char*, "", "s", "s", ""); -SFMT_FORMAT_FIXER(char*, "", "s", "s", ""); -SFMT_FORMAT_FIXER_TPL(size_t N, const char (&)[N], "", "s", "s", ""); -SFMT_FORMAT_FIXER_TPL(size_t N, char (&)[N], "", "s", "s", ""); -SFMT_FORMAT_FIXER_TPL(class Type, Type*, "", "p", "p", ""); +SFMT_FORMAT_FIXER(char, "-", "c", "c", ""); + +SFMT_FORMAT_FIXER(std::string, "-.", "s", "s", ""); +SFMT_FORMAT_FIXER(const char*, "-.", "s", "s", ""); +SFMT_FORMAT_FIXER(char*, "-.", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, const char (&)[N], "-.", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(size_t N, char (&)[N], "-.", "s", "s", ""); +SFMT_FORMAT_FIXER_TPL(class Type, Type*, "-", "p", "p", ""); #undef SFMT_FORMAT_FIXER_TPL #undef SFMT_FORMAT_FIXER +template +struct const_copy +{ + static void copy(char* target, const char *source) + { + *target = *source; + return const_copy::copy(target + 1, source + 1); + } +}; + +template<> +struct const_copy<1> // 1 because the last item is the terminal '\0'. +{ + static void copy(char*, const char *) { } // do nothing, terminal value +}; + +// These maps must cover all allowed values, for safety. +static const char present_int_map[8] = { 'i', 'o', 'x', 'X', 'd', 'd', 'd', 'd'}; +static const char present_float_map[8] = { 'g', 'G', 'a', 'A', 'e', 'E', 'f', 'g' }; + +} + +struct sfmc +{ + enum postype { pos_no, pos_plus, pos_space, pos_invalid }; + enum flavor { + flavor_dec = 0, + flavor_oct = 1, + flavor_hex = 2, + flavor_uhex = 3, + + flavor_general = 0, + flavor_ugeneral = 1, + flavor_fhex = 2, + flavor_ufhex = 3, + flavor_scientific = 4, + flavor_uscientific = 5, + flavor_fixed = 6 + }; + +protected: + short widthval; + short precisionval; + bool widthbit:1; + bool precisionbit:1; + bool altbit:1; + bool leftbit:1; + bool leadzerobit:1; + postype postype:2; + flavor presentation:3; + bool localized:1; + +public: + sfmc(): + widthval(0), + precisionval(6), + widthbit(false), + precisionbit(false), + altbit(false), + leftbit(false), + leadzerobit(false), + postype(pos_no), + presentation(flavor_dec), + localized(false) + { + } + +#define SFMTC_TAG(name, body) sfmc& name () { body; return *this; } +#define SFMTC_TAG_VAL(name, body) sfmc& name (int val) { body; return *this; } + + SFMTC_TAG(alt, altbit = true); + SFMTC_TAG(left, leftbit = true); + SFMTC_TAG(right, (void)0); + SFMTC_TAG_VAL(width, widthbit = true; widthval = abs(val)); + SFMTC_TAG_VAL(precision, precisionbit = true; precisionval = abs(val)); + SFMTC_TAG(dec, (void)0); + SFMTC_TAG(hex, presentation = flavor_hex); + SFMTC_TAG(oct, presentation = flavor_oct); + SFMTC_TAG(uhex, presentation = flavor_uhex); + SFMTC_TAG(general, (void)0); + SFMTC_TAG(ugeneral, presentation = flavor_ugeneral); + SFMTC_TAG(fhex, presentation = flavor_fhex); + SFMTC_TAG(ufhex, presentation = flavor_ufhex); + SFMTC_TAG(exp, presentation = flavor_scientific); + SFMTC_TAG(uexp, presentation = flavor_uscientific); + SFMTC_TAG(scientific, presentation = flavor_scientific); + SFMTC_TAG(uscientific, presentation = flavor_uscientific); + SFMTC_TAG(fixed, presentation = flavor_fixed); + SFMTC_TAG(nopos, (void)0); + SFMTC_TAG(posspace, postype = pos_space); + SFMTC_TAG(posplus, postype = pos_plus); + SFMTC_TAG(fillzero, leadzerobit = true); + +#undef SFMTC_TAG +#undef SFMTC_TAG_VAL + + // Utility function to store the number for width/precision + // For C++11 it could be constexpr, but this is C++03-compat code. + // It's bound to this structure because it's unsafe. + static size_t store_number(char* position, int number) + { + size_t shiftpos = 0; + div_t dm = div(number, 10); + if (dm.quot) + shiftpos = store_number(position, dm.quot); + position[shiftpos] = '0' + dm.rem; + return shiftpos + 1; + } + + template + internal::form_memory_buffer<> create_format_int(const char (&lnspec)[N]) const + { + using namespace internal; + + Ensure= 2> c3; (void)c3; + + form_memory_buffer<> form; + + // Use reservation as this will be faster and also + // we don't know how many ciphers will be taken by width/precision + // We need to reserve 12 + 12 + 4 + 4 = 32. + char* buf = form.expose(32); + size_t used = 0; + buf[used++] = '%'; + + if (altbit) + buf[used++] = '#'; + + if (leftbit) + buf[used++] = '-'; + + if (localized) + buf[used++] = '\''; + + if (leadzerobit) + buf[used++] = '0'; + + if (widthbit) + { + size_t w = store_number(buf + used, widthval); + used += w; + } + + // That's it, now we copy the type length modifier + const_copy::copy(buf + used, lnspec); + used += (N - 2); + + // And finally the flavor modifier + if (int(presentation) == 0) + buf[used++] = lnspec[N-2]; + else + buf[used++] = present_int_map[presentation]; + buf[used] = '\0'; + + size_t unused = 32 - used; + if (unused > 0) + form.unreserve(unused); + form.commit(); + + return form; + } + + template + internal::form_memory_buffer<> create_format_float(const char (&lnspec)[N]) const + { + using namespace internal; + Ensure= 2> c3; (void)c3; + + form_memory_buffer<> form; + + // Use reservation as this will be faster and also + // we don't know how many ciphers will be taken by width/precision + // We need to reserve 12 + 12 + 4 + 4 = 32. + char* buf = form.expose(32); + size_t used = 0; + buf[used++] = '%'; + + if (altbit) + buf[used++] = '#'; + + if (leftbit) + buf[used++] = '-'; + + if (localized) + buf[used++] = '\''; + + if (leadzerobit) + buf[used++] = '0'; + + if (widthbit) + { + size_t w = store_number(buf + used, widthval); + used += w; + } + + if (precisionbit) + { + buf[used++] = '.'; + size_t w = store_number(buf + used, precisionval); + used += w; + } + + // That's it, now we copy the type length modifier + const_copy::copy(buf + used, lnspec); + used += (N - 2); + + // And finally the flavor modifier + if (int(presentation) == 0) + buf[used++] = lnspec[N-2]; + else + buf[used++] = present_float_map[presentation]; + buf[used] = '\0'; + + size_t unused = 32 - used; + if (unused > 0) + form.unreserve(unused); + form.commit(); + + return form; + } + + internal::form_memory_buffer<> create_format(int) const { return create_format_int("i"); } + internal::form_memory_buffer<> create_format(short int) const { return create_format_int("hi"); }; + internal::form_memory_buffer<> create_format(long int) const { return create_format_int("li"); }; + internal::form_memory_buffer<> create_format(long long int) const { return create_format_int("lli"); }; + + internal::form_memory_buffer<> create_format(unsigned int) const { return create_format_int("u"); }; + internal::form_memory_buffer<> create_format(unsigned short int) const { return create_format_int("hu"); }; + internal::form_memory_buffer<> create_format(unsigned long int) const { return create_format_int("lu"); }; + internal::form_memory_buffer<> create_format(unsigned long long int) const { return create_format_int("llu"); }; + + internal::form_memory_buffer<> create_format(double) const { return create_format_float("g"); }; + internal::form_memory_buffer<> create_format(float) const { return create_format_float("g"); }; + internal::form_memory_buffer<> create_format(long double) const { return create_format_float("Lg"); }; + + internal::form_memory_buffer<> create_format(char) const + { + internal::form_memory_buffer<> form; + form.append("%c", 3); + return form; + } + + internal::form_memory_buffer<> create_format_string() const + { + using namespace internal; + + form_memory_buffer<> form; + + // Use reservation as this will be faster and also + // we don't know how many ciphers will be taken by width/precision + // We need to reserve 12 + 12 + 4 + 4 = 32. + char* buf = form.expose(32); + size_t used = 0; + buf[used++] = '%'; + + if (leftbit) + buf[used++] = '-'; + + if (widthbit) + { + size_t w = store_number(buf + used, widthval); + used += w; + } + + if (precisionbit) + { + buf[used++] = '.'; + size_t w = store_number(buf + used, precisionval); + used += w; + } + + // No modifier supported + // XXX NOTE: wchar_t therefore not supported! + buf[used++] = 's'; + buf[used] = '\0'; + + size_t unused = 32 - used; + if (unused > 0) + form.unreserve(unused); + form.commit(); + + return form; + } + + internal::form_memory_buffer<> create_format(char*) const { return create_format_string(); }; + internal::form_memory_buffer<> create_format(const char*) const { return create_format_string(); }; + internal::form_memory_buffer<> create_format(std::string) const { return create_format_string(); }; +}; + +// fmt(val, sfmc().alt().hex().width(10)) + +namespace internal +{ + template inline void write_default(Stream& str, const Value& val); @@ -628,25 +918,14 @@ static inline size_t SNPrintfOne(char* buf, size_t bufsize, const char* fmt, con { return std::snprintf(buf, bufsize, fmt, val.c_str()); } -} template inline -internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) +form_memory_buffer<> sfmt_imp(const Value& val, const form_memory_buffer<>& fstr, size_t bufsize) { - using namespace internal; - - form_memory_buffer<> fstr = apply_format_fix(val, fmtspec); form_memory_buffer<> out; - size_t bufsize = form_memory_buffer<>::INITIAL_SIZE; - // Reserve the maximum initial first, then shrink. char* buf = out.expose(bufsize); - // We want to use this buffer as a NUL-terminated string. - // So we need to add NUL character oursevles, form_memory_buffer<> - // doesn't do it an doesn't use the NUL-termination. - fstr.append('\0'); - size_t valsize = SNPrintfOne(buf, bufsize, fstr.get_first(), val); // Deemed impossible to happen, but still @@ -669,6 +948,35 @@ internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) out.commit(); return out; } +} + +template inline +internal::form_memory_buffer<> sfmt(const Value& val, const char* fmtspec = 0) +{ + using namespace internal; + + form_memory_buffer<> fstr = apply_format_fix(val, fmtspec); + size_t bufsize = form_memory_buffer<>::INITIAL_SIZE; + + // We want to use this buffer as a NUL-terminated string. + // So we need to add NUL character oursevles, form_memory_buffer<> + // doesn't do it an doesn't use the NUL-termination. + fstr.append('\0'); + + return sfmt_imp(val, fstr, bufsize); +} + +template inline +internal::form_memory_buffer<> sfmt(const Value& val, const sfmc& config) +{ + using namespace internal; + + form_memory_buffer<> fstr = config.create_format(val); + size_t bufsize = form_memory_buffer<>::INITIAL_SIZE; + return sfmt_imp(val, fstr, bufsize); +} + + namespace internal { @@ -677,21 +985,16 @@ void write_default(Stream& s, const Value& v) { s << sfmt(v, ""); } -} -template -std::string sfmts(const Value& val, const char* fmtspec = 0) +inline void sfmts_imp(const internal::form_memory_buffer<>& b, std::string& out) { using namespace internal; - std::string out; - form_memory_buffer<> b = sfmt(val, fmtspec); if (b.size() == 0) - return out; + return; out.reserve(b.size()); - out.append(b.get_first(), b.first_size()); for (form_memory_buffer<>::slices_t::const_iterator i = b.get_slices().begin(); i != b.get_slices().end(); ++i) @@ -699,10 +1002,26 @@ std::string sfmts(const Value& val, const char* fmtspec = 0) const char* data = &(*i)[0]; out.append(data, i->size()); } +} +} +template +inline std::string sfmts(const Value& val, const char* fmtspec = 0) +{ + std::string out; + internal::sfmts_imp(sfmt(val, fmtspec), (out)); return out; } +template +inline std::string sfmts(const Value& val, const sfmc& fmtspec) +{ + std::string out; + internal::sfmts_imp(sfmt(val, fmtspec), (out)); + return out; +} + + // Semi-manipulator to add the end-of-line. const internal::form_memory_buffer<2> seol ("\n"); diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index e4e511fb5..9dcaabb54 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -50,21 +50,17 @@ std::string FormatTime(const steady_clock::time_point& timestamp) const uint64_t hours = total_sec / (60 * 60) - days * 24; const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; - - // Temporary solution. Need to find some better handling - // of dynamic width and precision. - fmt::obufstream dfmts; - dfmts << "0" << decimals; - dfmts.append('\0'); // form_memory_buffer doesn't use NUL-termination. - const char* decimal_fmt = dfmts.bufptr(); - + steady_clock::time_point frac = timestamp - seconds_from(total_sec); fmt::obufstream out; if (days) out << days << "D "; + out << fmt::sfmt(hours, "02") << ":" << fmt::sfmt(minutes, "02") << ":" << fmt::sfmt(seconds, "02") << "." - << fmt::sfmt((timestamp - seconds_from(total_sec)).time_since_epoch().count(), decimal_fmt) << " [STDY]"; + << fmt::sfmt(frac.time_since_epoch().count(), + fmt::sfmc().fillzero().width(decimals)) + << " [STDY]"; return out.str(); } From a2b1b44b35c883ac176b5273856d1224bc5af089 Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Mon, 17 Jun 2024 11:40:52 +0200 Subject: [PATCH 140/517] First working version with end/drop as offset --- CMakeLists.txt | 2 +- srtcore/buffer_rcv.cpp | 611 +++++++++++++++++++++------------------ srtcore/buffer_rcv.h | 123 +++++--- test/test_buffer_rcv.cpp | 71 +++++ testing/testmedia.cpp | 24 +- 5 files changed, 493 insertions(+), 338 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5994d3b7..70e1f50a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1528,7 +1528,7 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) - gtest_discover_tests(test-srt) + #gtest_discover_tests(test-srt) endif() enable_testing() diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 539c51f9e..1562d0264 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -104,14 +104,14 @@ CRcvBuffer::CRcvBuffer(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool b , m_szSize(size) // TODO: maybe just use m_entries.size() , m_pUnitQueue(unitqueue) , m_iStartSeqNo(initSeqNo) // NOTE: SRT_SEQNO_NONE is allowed here. - , m_iStartPos(&m_szSize, 0) - , m_iEndPos(&m_szSize, 0) - , m_iDropPos(&m_szSize, 0) - , m_iFirstNonreadPos(&m_szSize, 0) + , m_iStartPos(0) + , m_iEndOff(0) + , m_iDropOff(0) + , m_iFirstNonreadPos(0) , m_iMaxPosOff(0) , m_iNotch(0) , m_numNonOrderPackets(0) - , m_iFirstNonOrderMsgPos(&m_szSize, CPos_TRAP.val()) + , m_iFirstNonOrderMsgPos(CPos_TRAP) , m_bPeerRexmitFlag(true) , m_bMessageAPI(bMessageAPI) , m_iBytesCount(0) @@ -138,8 +138,8 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) { HLOGC(brlog.Debug, log << "RCV-BUF-STATE(" << source << ") start=" << m_iStartPos VALUE - << " end=" << m_iEndPos VALUE - << " drop=" << m_iDropPos VALUE + << " end=+" << m_iEndOff VALUE + << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE << " seq[start]=%" << m_iStartSeqNo VALUE); } @@ -211,7 +211,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // Set to a value, if due to insertion there was added // a packet that is earlier to be retrieved than the earliest // currently available packet. - time_point earlier_time = updatePosInfo(unit, prev_max_off, newpktpos, extended_end); + time_point earlier_time = updatePosInfo(unit, prev_max_off, offset, extended_end); InsertInfo ireport (InsertInfo::INSERTED); ireport.first_time = earlier_time; @@ -237,102 +237,103 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) void CRcvBuffer::getAvailInfo(CRcvBuffer::InsertInfo& w_if) { - CPos fallback_pos = CPos_TRAP; - if (!m_tsbpd.isEnabled()) - { - // In case when TSBPD is off, we take into account the message mode - // where messages may potentially span for multiple packets, therefore - // the only "next deliverable" is the first complete message that satisfies - // the order requirement. - // NOTE THAT this field can as well be -1 already. - fallback_pos = m_iFirstNonOrderMsgPos; - } - else if (m_iDropPos != m_iEndPos) - { - // With TSBPD regard the drop position (regardless if - // TLPKTDROP is currently on or off), if "exists", that - // is, m_iDropPos != m_iEndPos. - fallback_pos = m_iDropPos; - } - - // This finds the first possible available packet, which is - // preferably at cell 0, but if not available, try also with - // given fallback position (unless it's -1). - const CPacket* pkt = tryAvailPacketAt(fallback_pos, (w_if.avail_range)); - if (pkt) - { - w_if.first_seq = CSeqNo(pkt->getSeqNo()); - } -} - + // This finds the first possible available packet, which is + // preferably at cell 0, but if not available, try also with + // given fallback position, if it's set + if (m_entries[m_iStartPos].status == EntryState_Avail) + { + const CPacket* pkt = &packetAt(m_iStartPos); + SRT_ASSERT(pkt); + w_if.avail_range = m_iEndOff; + w_if.first_seq = CSeqNo(pkt->getSeqNo()); + return; + } -const CPacket* CRcvBuffer::tryAvailPacketAt(CPos pos, COff& w_span) -{ - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - pos = m_iStartPos; - w_span = offPos(m_iStartPos, m_iEndPos); - //w_span = m_iEndPos - m_iStartPos; - } + // If not the first position, probe the skipped positions: + // - for live mode, check the DROP position + // (for potential after-drop reading) + // - for message mode, check the non-order message position + // (for potential out-of-oder message delivery) - if (pos == CPos_TRAP) - { - w_span = COff(0); - return NULL; - } + const CPacket* pkt = NULL; + if (m_tsbpd.isEnabled()) + { + // With TSBPD you can rely on drop position, if set + if (m_iDropOff) + { + pkt = &packetAt(incPos(m_iStartPos, m_iDropOff)); + SRT_ASSERT(pkt); + } + } + else + { + // Message-mode: try non-order read position. + if (m_iFirstNonOrderMsgPos != CPos_TRAP) + { + pkt = &packetAt(m_iFirstNonOrderMsgPos); + SRT_ASSERT(pkt); + } + } - SRT_ASSERT(m_entries[pos].pUnit != NULL); + if (!pkt) + { + // This is default, but set just in case + // The default seq is SRT_SEQNO_NONE. + w_if.avail_range = COff(0); + return; + } - // TODO: we know that at least 1 packet is available, but only - // with m_iEndPos we know where the true range is. This could also - // be implemented for message mode, but still this would employ - // a separate begin-end range declared for a complete out-of-order - // message. - w_span = COff(1); - return &packetAt(pos); + // TODO: we know that at least 1 packet is available, but only + // with m_iEndOff we know where the true range is. This could also + // be implemented for message mode, but still this would employ + // a separate begin-end range declared for a complete out-of-order + // message. + w_if.avail_range = COff(1); + w_if.first_seq = CSeqNo(pkt->getSeqNo()); } -CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end) + +// This function is called exclusively after packet insertion. +// This will update also m_iEndOff and m_iDropOff fields (the latter +// regardless of the TSBPD mode). +CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff prev_max_off, + const COff offset, + const bool extended_end) { time_point earlier_time; - CPos prev_max_pos = incPos(m_iStartPos, prev_max_off); - //CPos prev_max_pos = m_iStartPos + prev_max_off; - // Update flags // Case [A] if (extended_end) { // THIS means that the buffer WAS CONTIGUOUS BEFORE. - if (m_iEndPos == prev_max_pos) + if (m_iEndOff == prev_max_off) { // THIS means that the new packet didn't CAUSE a gap if (m_iMaxPosOff == prev_max_off + 1) { - // This means that m_iEndPos now shifts by 1, - // and m_iDropPos must be shifted together with it, + // This means that m_iEndOff now shifts by 1, + // and m_iDropOff must be shifted together with it, // as there's no drop to point. - m_iEndPos = incPos(m_iStartPos, m_iMaxPosOff); - //m_iEndPos = m_iStartPos + m_iMaxPosOff; - m_iDropPos = m_iEndPos; + m_iEndOff = m_iMaxPosOff; + m_iDropOff = 0; } else { // Otherwise we have a drop-after-gap candidate // which is the currently inserted packet. - // Therefore m_iEndPos STAYS WHERE IT IS. - m_iDropPos = incPos(m_iStartPos, m_iMaxPosOff - 1); - //m_iDropPos = m_iStartPos + (m_iMaxPosOff - 1); + // Therefore m_iEndOff STAYS WHERE IT IS. + m_iDropOff = m_iMaxPosOff - 1; } } } // - // Since this place, every newpktpos is in the range - // between m_iEndPos (inclusive) and a position for m_iMaxPosOff. + // Since this place, every 'offset' is in the range + // between m_iEndOff (inclusive) and m_iMaxPosOff. // Here you can use prev_max_pos as the position represented // by m_iMaxPosOff, as if !extended_end, it was unchanged. - else if (newpktpos == m_iEndPos) + else if (offset == m_iEndOff) { // Case [D]: inserted a packet at the first gap following the // contiguous region. This makes a potential to extend the @@ -342,22 +343,14 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // new earliest packet now. In any other situation under this // condition there's some contiguous packet range preceding // this position. - if (m_iEndPos == m_iStartPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } - updateGapInfo(prev_max_pos); + updateGapInfo(); } - // XXX Not sure if that's the best performant comparison - // What is meant here is that newpktpos is between - // m_iEndPos and m_iDropPos, though we know it's after m_iEndPos. - // CONSIDER: make m_iDropPos rather m_iDropOff, this will make - // this comparison a simple subtraction. Note that offset will - // have to be updated on every shift of m_iStartPos. - - //else if (cmpPos(newpktpos, m_iDropPos) < 0) - else if (newpktpos.cmp(m_iDropPos, m_iStartPos) < 0) + else if (offset < m_iDropOff) { // Case [C]: the newly inserted packet precedes the // previous earliest delivery position after drop, @@ -370,10 +363,10 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // // We know it because if the position has filled a gap following // a valid packet, this preceding valid packet would be pointed - // by m_iDropPos, or it would point to some earlier packet in a + // by m_iDropOff, or it would point to some earlier packet in a // contiguous series of valid packets following a gap, hence // the above condition wouldn't be satisfied. - m_iDropPos = newpktpos; + m_iDropOff = offset; // If there's an inserted packet BEFORE drop-pos (which makes it // a new drop-pos), while the very first packet is absent (the @@ -382,7 +375,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position, but still following some earlier contiguous range // of valid packets - so it's earlier than previous drop, but // not earlier than the earliest packet. - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { earlier_time = getPktTsbPdTime(unit->m_Packet.getMsgTimeStamp()); } @@ -392,37 +385,94 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p return earlier_time; } -void CRcvBuffer::updateGapInfo(CPos prev_max_pos) +// This function is called when the m_iEndOff has been set to a new +// position and the m_iDropOff should be calculated since that position again. +void CRcvBuffer::updateGapInfo() { - CPos pos = m_iEndPos; + COff from = m_iEndOff, to = m_iMaxPosOff; + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); - // First, search for the next gap, max until m_iMaxPosOff. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + CPos pos = incPos(m_iStartPos, from); + + if (m_entries[pos].status == EntryState_Avail) { - if (m_entries[pos].status == EntryState_Empty) + + CPos end_pos = incPos(m_iStartPos, m_iMaxPosOff); + + for (; pos != end_pos; pos = incPos(pos)) { - break; + if (m_entries[pos].status != EntryState_Avail) + break; } + + m_iEndOff = offPos(m_iStartPos, pos); } - if (pos == prev_max_pos) + + // XXX This should be this way, but there are still inconsistencies + // in the message code. + //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); + + // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP + // is set. This option is not handled in message mode, only in live mode. + // Dropping by packet makes sense only in case of packetwise reading, + // which isn't the case of neither stream nor message mode. + if (!m_tsbpd.isEnabled()) { - // Reached the end and found no gaps. - m_iEndPos = prev_max_pos; - m_iDropPos = prev_max_pos; + m_iDropOff = 0; + return; } - else + + // Do not touch m_iDropOff if it's still beside the contiguous + // region. DO NOT SEARCH for m_iDropOff if m_iEndOff is max + // because this means that the whole buffer is contiguous. + // That would simply find nothing and only uselessly burden the + // performance by searching for a not present empty cell. + + // Also check if the current drop position is a readable packet. + // If not, start over. + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + + if (m_iDropOff < m_iEndOff || m_entries[drop_pos].status != EntryState_Avail) { - // Found a gap at pos - m_iEndPos = pos; - m_iDropPos = pos; // fallback, although SHOULD be impossible - // So, search for the first position to drop up to. - for ( ; pos != prev_max_pos; ++pos /*pos = incPos(pos)*/) + m_iDropOff = 0; + if (m_iEndOff < m_iMaxPosOff) { - if (m_entries[pos].status != EntryState_Empty) + int maxend = m_szSize - m_iStartPos VALUE; + int ifrom = m_iEndOff + 1; + int ito = m_iMaxPosOff VALUE; + + bool found = false; + for (int i = ifrom; i < std::min(maxend, ito); ++i) { - m_iDropPos = pos; - break; + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } } + + if (!found && ito > maxend) + { + int upto = ito - maxend; + for (int i = 0; i < upto; ++i) + { + if (m_entries[CPos(i)].status == EntryState_Avail) + { + m_iDropOff = i; + found = true; + break; + } + } + } + + // Must be found somewhere, worst case at the position + // of m_iMaxPosOff-1. If no finding loop caught it somehow, + // it will remain at 0. The case when you have empty packets + // in the busy range is only with message mode after reading + // packets out-of-order, but this doesn't use tsbpd mode. + SRT_ASSERT(m_iDropOff != 0); } } } @@ -444,9 +494,9 @@ int CRcvBuffer::dropUpTo(int32_t seqno) return 0; } - m_iMaxPosOff -= len; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + m_iMaxPosOff = decOff(m_iMaxPosOff, len); + m_iEndOff = decOff(m_iEndOff, len); + m_iDropOff = decOff(m_iDropOff, len); const int iDropCnt = len VALUE; while (len VALUE > 0) @@ -454,8 +504,8 @@ int CRcvBuffer::dropUpTo(int32_t seqno) dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); - //m_iStartPos = incPos(m_iStartPos); - ++m_iStartPos; + m_iStartPos = incPos(m_iStartPos); + //++m_iStartPos; --len; } @@ -465,10 +515,7 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // (This call MAY shift m_iStartSeqNo further.) releaseNextFillerEntries(); - // Start from here and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(incPos(m_iStartPos, m_iMaxPosOff)); - //updateGapInfo(m_iStartPos + m_iMaxPosOff); + updateGapInfo(); // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. @@ -538,7 +585,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (bKeepExisting && bnd == PB_SOLO) { bDropByMsgNo = false; // Solo packet, don't search for the rest of the message. - LOGC(rbuflog.Debug, + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage(): Skipped dropping an existing SOLO packet %" << packetAt(i).getSeqNo() << "."); continue; @@ -565,6 +612,14 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro //minDroppedOffset = i - m_iStartPos; } + if (end_off > m_iMaxPosOff) + { + HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " - updating the busy region"); + m_iMaxPosOff = end_off; + } + if (bDropByMsgNo) { // If msgno is specified, potentially not the whole message was dropped using seqno range. @@ -574,7 +629,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro // Try to drop by the message number in case the message starts earlier than @a seqnolo. const CPos stop_pos = decPos(m_iStartPos); //const CPos stop_pos = m_iStartPos - COff(1); - for (CPos i = start_pos; i != stop_pos; --i) + for (CPos i = start_pos; i != stop_pos; i = decPos(i)) { // Can't drop if message number is not known. if (!m_entries[i].pUnit) // also dropped earlier. @@ -607,14 +662,23 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro IF_RCVBUF_DEBUG(scoped_log.ss << " iDropCnt " << iDropCnt); } + if (iDropCnt) + { + // We don't need the drop position, if we allow to drop messages by number + // and with that value we risk that drop was pointing to a dropped packet. + // Theoretically to make it consistent we need to shift the value to the + // next found packet, but we don't need this information if we use the message + // mode (because drop-by-packet is not supported in this mode) and this + // will burden the performance for nothing. + m_iDropOff = 0; + } + // Check if units before m_iFirstNonreadPos are dropped. const bool needUpdateNonreadPos = (minDroppedOffset != -1 && minDroppedOffset <= getRcvDataSize()); releaseNextFillerEntries(); - // XXX TEST AND FIX - // Start from the last updated start pos and search fort the next gap - m_iEndPos = m_iDropPos = m_iStartPos; - updateGapInfo(end_pos); + updateGapInfo(); + IF_HEAVY_LOGGING(debugShowState( ("dropmsg off %" + Sprint(seqnolo) + " #" + Sprint(msgno)).c_str())); @@ -636,7 +700,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const { - if (m_iStartPos == m_iEndPos) + if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); @@ -644,18 +708,15 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - - //w_seq = CSeqNo::incseq(m_iStartSeqNo, end_off); - w_seq = (m_iStartSeqNo + end_off VALUE) VALUE; + w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << end_off VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE << " base=%" << m_iStartSeqNo VALUE << " end=%" << w_seq); - return (end_off < m_iMaxPosOff); + return (m_iEndOff < m_iMaxPosOff); } int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair* pw_seqrange) @@ -681,7 +742,17 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); m_iStartSeqNo = CSeqNo(pktseqno) + 1; + ++nskipped; } else { @@ -759,72 +814,39 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 0); + + m_iEndOff = decOff(m_iEndOff, len); + } countBytes(-pkts_read, -bytes_extracted); releaseNextFillerEntries(); + // This will update the end position + updateGapInfo(); + if (!isInUsedRange( m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; //updateNonreadPos(); } - // Now that we have m_iStartPos potentially shifted, reinitialize - // m_iEndPos and m_iDropPos. - - CPos pend_pos = incPos(m_iStartPos, m_iMaxPosOff); - //CPos pend_pos = m_iStartPos + m_iMaxPosOff; - - // First check: is anything in the beginning - if (m_entries[m_iStartPos].status == EntryState_Avail) - { - // If so, shift m_iEndPos up to the first nonexistent unit - // XXX Try to optimize search by splitting into two loops if necessary. - - m_iEndPos = incPos(m_iStartPos); - //m_iEndPos = m_iStartPos + COff(1); - while (m_entries[m_iEndPos].status == EntryState_Avail) - { - m_iEndPos = incPos(m_iEndPos); - //m_iEndPos = m_iEndPos + COff(1); - if (m_iEndPos == pend_pos) - break; - } - - // If we had first packet available, then there's also no drop pos. - m_iDropPos = m_iEndPos; - } - else - { - // If not, reset m_iEndPos and search for the first after-drop candidate. - m_iEndPos = m_iStartPos; - m_iDropPos = m_iEndPos; - - // The container could have become empty. - // Stay here if so. - if (m_iStartPos != pend_pos) - { - while (m_entries[m_iDropPos].status != EntryState_Avail) - { - m_iDropPos = incPos(m_iDropPos); - //m_iDropPos = m_iDropPos + COff(1); - if (m_iDropPos == pend_pos) - { - // Nothing found - set drop pos equal to end pos, - // which means there's no drop - m_iDropPos = m_iEndPos; - break; - } - } - } - } - - if (!m_tsbpd.isEnabled()) // We need updateFirstReadableNonOrder() here even if we are reading inorder, // incase readable inorder packets are all read out. @@ -923,6 +945,15 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + + --m_iEndOff; + if (m_iEndOff < 0) + m_iEndOff = 0; + + --m_iDropOff; + if (m_iDropOff < 0) + m_iDropOff = 0; + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; } @@ -1043,10 +1074,11 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const pkt = &packetAt(m_iStartPos); } // If not, get the information from the drop - else if (m_iDropPos != m_iEndPos) + else if (m_iDropOff) { - SRT_ASSERT(m_entries[m_iDropPos].pUnit); - pkt = &packetAt(m_iDropPos); + CPos drop_pos = incPos(m_iStartPos, m_iDropOff); + SRT_ASSERT(m_entries[drop_pos].pUnit); + pkt = &packetAt(drop_pos); pi.seq_gap = true; // Available, but after a drop. } else @@ -1165,21 +1197,43 @@ bool CRcvBuffer::dropUnitInPos(CPos pos) return true; } -void CRcvBuffer::releaseNextFillerEntries() +int CRcvBuffer::releaseNextFillerEntries() { CPos pos = m_iStartPos; + int nskipped = 0; + while (m_entries[pos].status == EntryState_Read || m_entries[pos].status == EntryState_Drop) { + if (nskipped == m_iMaxPosOff) + { + // This should never happen. All the previously read- or drop-marked + // packets should be contained in the range up to m_iMaxPosOff. Do not + // let the buffer ride any further and report the problem. Still stay there. + LOGC(rbuflog.Error, log << "releaseNextFillerEntries: IPE: Read/Drop status outside the busy range!"); + break; + } + //m_iStartSeqNo = CSeqNo::incseq(m_iStartSeqNo); ++m_iStartSeqNo; releaseUnitInPos(pos); - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; m_iStartPos = pos; - --m_iMaxPosOff; - if (m_iMaxPosOff < 0) - m_iMaxPosOff = 0; + ++nskipped; } + + if (!nskipped) + { + return nskipped; + } + + m_iMaxPosOff -= nskipped; + m_iEndOff = decOff(m_iEndOff, nskipped); + + // Drop off will be updated after that call, if needed. + m_iDropOff = 0; + + return nskipped; } // TODO: Is this function complete? There are some comments left inside. @@ -1197,7 +1251,7 @@ void CRcvBuffer::updateNonreadPos() if (m_bMessageAPI && (packetAt(pos).getMsgBoundary() & PB_FIRST) == 0) break; - for (CPos i = pos; i != end_pos; ++i) // i = incPos(i)) + for (CPos i = pos; i != end_pos; i = incPos(i)) { if (!m_entries[i].pUnit || m_entries[pos].status != EntryState_Avail) { @@ -1226,7 +1280,7 @@ void CRcvBuffer::updateNonreadPos() CPos CRcvBuffer::findLastMessagePkt() { - for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; ++i) //i = incPos(i)) + for (CPos i = m_iStartPos; i != m_iFirstNonreadPos; i = incPos(i)) { SRT_ASSERT(m_entries[i].pUnit); @@ -1333,7 +1387,7 @@ void CRcvBuffer::updateFirstReadableNonOrder() CPos posLast = CPos_TRAP; int msgNo = -1; - for (CPos pos = m_iStartPos; outOfOrderPktsRemain; ++pos) //pos = incPos(pos)) + for (CPos pos = m_iStartPos; outOfOrderPktsRemain; pos = incPos(pos)) { if (!m_entries[pos].pUnit) { @@ -1393,8 +1447,8 @@ CPos CRcvBuffer::scanNonOrderMessageRight(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = incPos(pos); - ++pos; + pos = incPos(pos); + //++pos; if (!m_entries[pos].pUnit) break; @@ -1424,8 +1478,8 @@ CPos CRcvBuffer::scanNonOrderMessageLeft(const CPos startPos, int msgNo) const CPos pos = startPos; do { - //pos = decPos(pos); - --pos; + pos = decPos(pos); + //--pos; if (!m_entries[pos].pUnit) return CPos_TRAP; @@ -1549,100 +1603,89 @@ void CRcvBuffer::updRcvAvgDataSize(const steady_clock::time_point& now) int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { + // This means that there are no lost seqs at all, no matter + // from which position they would have to be checked. + if (m_iEndOff == m_iMaxPosOff) + return SRT_SEQNO_NONE; + //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; COff offset (offset_val); - // Check if it's still inside the buffer - if (offset_val < 0 || offset >= m_iMaxPosOff) + // Check if it's still inside the buffer. + // Skip the region from 0 to m_iEndOff because this + // region is by definition contiguous and contains no loss. + if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } - // Start position - CPos frompos = incPos(m_iStartPos, offset); - //CPos frompos = m_iStartPos + offset; - - // Ok; likely we should stand at the m_iEndPos position. - // If this given position is earlier than this, then - // m_iEnd stands on the first loss, unless it's equal - // to the position pointed by m_iMaxPosOff. - - CSeqNo ret_seq = CSeqNo(SRT_SEQNO_NONE); - COff ret_off = m_iMaxPosOff; - COff end_off = offPos(m_iStartPos, m_iEndPos); - //COff end_off = m_iEndPos - m_iStartPos; - if (offset < end_off) + // Check if this offset is equal to m_iEndOff. If it is, + // then you have the loss sequence exactly the one that + // was passed. Skip now, pw_end was not requested. + if (offset == m_iEndOff) { - // If m_iEndPos has such a value, then there are - // no loss packets at all. - if (end_off != m_iMaxPosOff) + if (pw_end) { - //ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, end_off VALUE); - ret_seq = m_iStartSeqNo + end_off VALUE; - ret_off = end_off; + // If the offset is exactly at m_iEndOff, then + // m_iDropOff will mark the end of gap. + if (m_iDropOff) + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + else + { + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); + *pw_end = fromseq; + } } + return fromseq; } - else - { - // Could be strange, but just as the caller wishes: - // find the first loss since this point on - // You can't rely on m_iEndPos, you are beyond that now. - // So simply find the next hole. - // REUSE offset as a control variable - for (; offset < m_iMaxPosOff; ++offset) + int ret_seq = SRT_SEQNO_NONE; + int loss_off = 0; + // Now find the first empty position since here, + // up to m_iMaxPosOff. Checking against m_iDropOff + // makes no sense because if it is not 0, you'll + // find it earlier by checking packet presence. + for (int off = offset; off < m_iMaxPosOff; ++off) + { + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status == EntryState_Empty) { - const CPos pos = incPos(m_iStartPos, offset); - //const CPos pos = m_iStartPos + offset; - if (m_entries[pos].status == EntryState_Empty) - { - ret_off = offset; - //ret_seq = CSeqNo::incseq(m_iStartSeqNo, offset); - ret_seq = m_iStartSeqNo + offset VALUE; - break; - } + ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + loss_off = off; + break; } } - // If found no loss, just return this value and do not - // rewrite nor look for anything. - - // Also no need to search anything if only the beginning was - // being looked for. - if (ret_seq == CSeqNo(SRT_SEQNO_NONE) || !pw_end) - return ret_seq VALUE; - - // We want also the end range, so continue from where you - // stopped. + if (ret_seq == SRT_SEQNO_NONE) + { + // This is theoretically possible if we search from behind m_iEndOff, + // after m_iDropOff. This simply means that we are trying to search + // behind the last gap in the buffer. + return ret_seq; + } - // Start from ret_off + 1 because we know already that ret_off - // points to an empty cell. - for (COff off = ret_off + COff(1); off < m_iMaxPosOff; ++off) + // We get this position, so search for the end of gap + if (pw_end) { - const CPos pos = incPos(m_iStartPos, off); - //const CPos pos = m_iStartPos + off; - if (m_entries[pos].status != EntryState_Empty) + for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - //*pw_end = CSeqNo::incseq(m_iStartSeqNo, (off - 1) VALUE); - *pw_end = (m_iStartSeqNo + (off - COff(1)) VALUE) VALUE; - return ret_seq VALUE; + CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + if (m_entries[ipos].status != EntryState_Empty) + { + *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + return ret_seq; + } } - } - // Fallback - this should be impossible, so issue a log. - LOGC(rbuflog.Error, log << "IPE: empty cell pos=" << frompos VALUE << " %" - //<< CSeqNo::incseq(m_iStartSeqNo, ret_off) - << (m_iStartSeqNo + ret_off VALUE) VALUE - << " not followed by any valid cell"); - - // Return this in the last resort - this could only be a situation when - // a packet has somehow disappeared, but it contains empty cells up to the - // end of buffer occupied range. This shouldn't be possible at all because - // there must be a valid packet at least at the last occupied cell. - return SRT_SEQNO_NONE; + // Should not be possible to not find an existing packet + // following the gap, otherwise there would be no gap. + LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: gap since %" << ret_seq << " not covered by existing packet"); + *pw_end = ret_seq; + } + return ret_seq; } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index ff659a45c..230efa481 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -29,22 +29,39 @@ namespace srt struct CPos { int value; +#if USE_OPERATORS const size_t* psize; - int isize() const {return *psize;} +#endif + +#if USE_OPERATORS + explicit CPos(const size_t* ps SRT_ATR_UNUSED, int val) + : value(val) + , psize(ps) + {} + +#else + explicit CPos(int val): value(val) {} +#endif - explicit CPos(const size_t* ps, int val): value(val), psize(ps) {} int val() const { return value; } explicit operator int() const {return value;} - CPos(const CPos& src): value(src.value), psize(src.psize) {} + CPos(const CPos& src): value(src.value) +#if USE_OPERATORS + , psize(src.psize) +#endif + {} CPos& operator=(const CPos& src) { +#if USE_OPERATORS psize = src.psize; +#endif value = src.value; return *this; } +#if USE_OPERATORS int cmp(CPos other, CPos start) const { int pos2 = value; @@ -72,6 +89,7 @@ struct CPos value = 0; return *this; } +#endif bool operator == (CPos other) const { return value == other.value; } bool operator != (CPos other) const { return value != other.value; } @@ -116,6 +134,15 @@ struct COff bool operator > (int other) const { return value > other; } bool operator <= (int other) const { return value <= other; } bool operator >= (int other) const { return value >= other; } + + friend bool operator == (int value, COff that) { return value == that.value; } + friend bool operator != (int value, COff that) { return value != that.value; } + friend bool operator < (int value, COff that) { return value < that.value; } + friend bool operator > (int value, COff that) { return value > that.value; } + friend bool operator <= (int value, COff that) { return value <= that.value; } + friend bool operator >= (int value, COff that) { return value >= that.value; } + + operator bool() const { return value != 0; } }; #define VALUE .val() @@ -161,8 +188,7 @@ inline CSeqNo operator-(CSeqNo seq, COff off) #endif -const size_t fix_rollover = 16; -const CPos CPos_TRAP (&fix_rollover, -1); +const CPos CPos_TRAP (-1); #else #define VALUE @@ -174,22 +200,33 @@ const int CPos_TRAP = -1; // // Circular receiver buffer. // +// ICR = Initial Contiguous Region: all cells here contain valid packets +// SCRAP REGION: Region with possibly filled or empty cells +// NOTE: in scrap region, the first cell is empty and the last one filled. +// SPARE REGION: Region without packets +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ -// | | | | -// | | | \__last pkt received -// | | | -// | | \___ m_iDropPos -// | | -// | \___ m_iEndPos -// | -// \___ m_iStartPos: first packet position in the buffer -// -// m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?) +// | | | | +// | | | \__last pkt received +// |<------------->| m_iDropOff | +// | | | +// |<--------->| m_iEndOff | +// | +// \___ m_iStartPos: first packet position in the buffer +// +// m_pUnit[i]->status: +// EntryState_Empty: No packet was ever received here +// EntryState_Avail: The packet is ready for reading +// EntryState_Read: The packet is non-order-read +// EntryState_Drop: The packet was requested to drop // // thread safety: // m_iStartPos: CUDT::m_RecvLock @@ -199,30 +236,30 @@ const int CPos_TRAP = -1; // // // m_iStartPos: the first packet that should be read (might be empty) -// m_iEndPos: the end of contiguous range. Empty if m_iEndPos == m_iStartPos -// m_iDropPos: a packet available for retrieval after a drop. If == m_iEndPos, no such packet. +// m_iEndOff: shift to the end of contiguous range. This points always to an empty cell. +// m_iDropPos: shift a packet available for retrieval after a drop. If 0, no such packet. // // Operational rules: // // Initially: // m_iStartPos = 0 -// m_iEndPos = 0 -// m_iDropPos = 0 +// m_iEndOff = 0 +// m_iDropOff = 0 // // When a packet has arrived, then depending on where it landed: // // 1. Position: next to the last read one and newest // // m_iStartPos unchanged. -// m_iEndPos shifted by 1 -// m_iDropPos = m_iEndPos +// m_iEndOff shifted by 1 +// m_iDropOff = 0 // // 2. Position: after a loss, newest. // // m_iStartPos unchanged. -// m_iEndPos unchanged. -// m_iDropPos: -// - if it was == m_iEndPos, set to this +// m_iEndOff unchanged. +// m_iDropOff: +// - set to this packet, if m_iDropOff == 0 or m_iDropOff is past this packet // - otherwise unchanged // // 3. Position: after a loss, but belated (retransmitted) -- not equal to m_iEndPos @@ -417,8 +454,7 @@ class CRcvBuffer /// InsertInfo insert(CUnit* unit); - time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const CPos newpktpos, const bool extended_end); - const CPacket* tryAvailPacketAt(CPos pos, COff& w_span); + time_point updatePosInfo(const CUnit* unit, const COff prev_max_off, const COff offset, const bool extended_end); void getAvailInfo(InsertInfo& w_if); /// Update the values of `m_iEndPos` and `m_iDropPos` in @@ -436,12 +472,7 @@ class CRcvBuffer /// update the m_iEndPos and m_iDropPos fields, or set them /// both to the end of range if there are no loss gaps. /// - /// The @a prev_max_pos parameter is passed here because it is already - /// calculated in insert(), otherwise it would have to be calculated here again. - /// - /// @param prev_max_pos buffer position represented by `m_iMaxPosOff` - /// - void updateGapInfo(CPos prev_max_pos); + void updateGapInfo(); /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number @@ -643,9 +674,9 @@ class CRcvBuffer private: //* - inline CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos(&m_szSize, (pos VALUE + inc VALUE) % m_szSize); } - inline CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(&m_szSize, pos VALUE - 1) : CPos(&m_szSize, m_szSize - 1); } - inline COff offPos(CPos pos1, CPos pos2) const + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } + CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + COff offPos(CPos pos1, CPos pos2) const { int diff = pos2 VALUE - pos1 VALUE; if (diff >= 0) @@ -655,7 +686,15 @@ class CRcvBuffer return COff(m_szSize + diff); } - inline COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + COff posToOff(CPos pos) const { return offPos(m_iStartPos, pos); } + + static COff decOff(COff val, int shift) + { + int ival = val VALUE - shift; + if (ival < 0) + return COff(0); + return COff(ival); + } /// @brief Compares the two positions in the receiver buffer relative to the starting position. /// @param pos2 a position in the receiver buffer. @@ -678,7 +717,7 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos(iStartPos.psize, (iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); + const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); //const CPos iLastPos = iStartPos + iMaxPosOff; const bool isOverrun = iLastPos VALUE < iStartPos VALUE; @@ -717,7 +756,9 @@ class CRcvBuffer /// Release entries following the current buffer position if they were already /// read out of order (EntryState_Read) or dropped (EntryState_Drop). - void releaseNextFillerEntries(); + /// + /// @return the range for which the start pos has been shifted + int releaseNextFillerEntries(); bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); } @@ -790,8 +831,8 @@ class CRcvBuffer CSeqNo m_iStartSeqNo; CPos m_iStartPos; // the head position for I/O (inclusive) - CPos m_iEndPos; // past-the-end of the contiguous region since m_iStartPos - CPos m_iDropPos; // points past m_iEndPos to the first deliverable after a gap, or == m_iEndPos if no such packet + COff m_iEndOff; // past-the-end of the contiguous region since m_iStartOff + COff m_iDropOff; // points past m_iEndOff to the first deliverable after a gap, or == m_iEndOff if no such packet CPos m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos) COff m_iMaxPosOff; // the furthest data position int m_iNotch; // index of the first byte to read in the first ready-to-read packet (used in file/stream mode) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 10be243a9..9436fa77d 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -646,6 +646,77 @@ TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop) EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity()); } +TEST_F(CRcvBufferReadMsg, MsgOrderScraps) +{ + // Ok, in this test we're filling the message this way: + // 1. We have an empty packet in the first cell. + // 2. This is followed by a 5-packet message that is valid. + // 3. This is followed by empty, valid, empty, valid, valid packet, + // where all valid packets belong to the same message. + // 4. After that there should be 3-packet valid messsage. + // 5. We deploy drop request to that second scrapped message. + // 6. We read one message. Should be the first message. + // 7. We read one message. Should be the last message. + + auto& rcv_buffer = *m_rcv_buffer.get(); + + // 1, 2 + addMessage(5,// packets + 2, // msgno + m_init_seqno + 1, + true); + + // LAYOUT: 10 11 12 13 + // [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + // * (2 2 2 2 2) * 3 * 3 3) (4 4 4) + + // 3 + addPacket( + m_init_seqno + 7, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 9, + 3, + false, false, // subsequent + true); + + addPacket( + m_init_seqno + 10, + 3, + false, true, // last + true); + + // 4 + addMessage(3, // packets + 4, // msgno + m_init_seqno + 11, + true); + + // 5 + EXPECT_GT(rcv_buffer.dropMessage(m_init_seqno+8, m_init_seqno+8, 3, CRcvBuffer::KEEP_EXISTING), 0); + + // 6 + array buff; + SRT_MSGCTRL mc; + pair seqrange; + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 2); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); + + CRcvBuffer::InsertInfo ii; + rcv_buffer.getAvailInfo((ii)); + EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); + + // 7 + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_EQ(mc.msgno, 4); + EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); + +} + // One message (4 packets) is added to the buffer after a message with "in order" flag. // Read in order TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder) diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 2d6635288..1bf63ed64 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -1059,8 +1059,8 @@ void SrtCommon::OpenGroupClient() if (!extras.empty()) { Verb() << "?" << extras[0] << VerbNoEOL; - for (size_t i = 1; i < extras.size(); ++i) - Verb() << "&" << extras[i] << VerbNoEOL; + for (size_t x = 1; x < extras.size(); ++x) + Verb() << "&" << extras[x] << VerbNoEOL; } Verb(); @@ -1130,15 +1130,15 @@ void SrtCommon::OpenGroupClient() // spread the setting on all sockets. ConfigurePost(m_sock); - for (size_t i = 0; i < targets.size(); ++i) + for (size_t x = 0; x < targets.size(); ++x) { // As m_group_nodes is simply transformed into 'targets', // one index can be used to index them all. You don't // have to check if they have equal addresses because they // are equal by definition. - if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS) + if (targets[x].id != -1 && targets[x].errorcode == SRT_SUCCESS) { - m_group_nodes[i].socket = targets[i].id; + m_group_nodes[x].socket = targets[x].id; } } @@ -1159,12 +1159,12 @@ void SrtCommon::OpenGroupClient() } m_group_data.resize(size); - for (size_t i = 0; i < m_group_nodes.size(); ++i) + for (size_t x = 0; x < m_group_nodes.size(); ++x) { - SRTSOCKET insock = m_group_nodes[i].socket; + SRTSOCKET insock = m_group_nodes[x].socket; if (insock == -1) { - Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; + Verb() << "TARGET '" << sockaddr_any(targets[x].peeraddr).str() << "' connection failed."; continue; } @@ -1194,11 +1194,11 @@ void SrtCommon::OpenGroupClient() NULL, NULL) != -1) { Verb() << "[C]" << VerbNoEOL; - for (int i = 0; i < len1; ++i) - Verb() << " " << ready_conn[i] << VerbNoEOL; + for (int x = 0; x < len1; ++x) + Verb() << " " << ready_conn[x] << VerbNoEOL; Verb() << "[E]" << VerbNoEOL; - for (int i = 0; i < len2; ++i) - Verb() << " " << ready_err[i] << VerbNoEOL; + for (int x = 0; x < len2; ++x) + Verb() << " " << ready_err[x] << VerbNoEOL; Verb() << ""; From ff16e6871036a25622b47e92fe7867284cd7596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Mon, 17 Jun 2024 13:52:01 +0200 Subject: [PATCH 141/517] Blocked development support types and fixed the test --- srtcore/buffer_rcv.cpp | 32 ++++++++++++++++---------------- srtcore/buffer_rcv.h | 17 ++++++++++------- srtcore/common.h | 8 ++++++++ test/test_buffer_rcv.cpp | 4 ++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 1562d0264..494dc3763 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -141,7 +141,7 @@ void CRcvBuffer::debugShowState(const char* source SRT_ATR_UNUSED) << " end=+" << m_iEndOff VALUE << " drop=+" << m_iDropOff VALUE << " max-off=+" << m_iMaxPosOff VALUE - << " seq[start]=%" << m_iStartSeqNo VALUE); + << " seq[start]=%" << m_iStartSeqNo.val()); } CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) @@ -389,7 +389,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff, to = m_iMaxPosOff; + COff from = m_iEndOff; //, to = m_iMaxPosOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -536,8 +536,8 @@ int CRcvBuffer::dropAll() if (empty()) return 0; - //const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; + const int32_t end_seqno = CSeqNo::incseq(m_iStartSeqNo.val(), m_iMaxPosOff); + //const int end_seqno = (m_iStartSeqNo + m_iMaxPosOff VALUE) VALUE; return dropUpTo(end_seqno); } @@ -556,7 +556,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (offset_b < 0) { LOGC(rbuflog.Debug, log << "CRcvBuffer.dropMessage(): nothing to drop. Requested [" << seqnolo << "; " - << seqnohi << "]. Buffer start " << m_iStartSeqNo VALUE << "."); + << seqnohi << "]. Buffer start " << m_iStartSeqNo.val() << "."); return 0; } @@ -615,7 +615,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro if (end_off > m_iMaxPosOff) { HLOGC(rbuflog.Debug, log << "CRcvBuffer::dropMessage: requested to drop up to %" << seqnohi - << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo VALUE, end_off) + << " with highest in the buffer %" << CSeqNo::incseq(m_iStartSeqNo.val(), end_off) << " - updating the busy region"); m_iMaxPosOff = end_off; } @@ -703,17 +703,17 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const if (m_iEndOff == 0) { // Initial contiguous region empty (including empty buffer). - HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo VALUE); - w_seq = m_iStartSeqNo VALUE; + HLOGC(rbuflog.Debug, log << "CONTIG: empty, give up base=%" << m_iStartSeqNo.val()); + w_seq = m_iStartSeqNo.val(); return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); //w_seq = (m_iStartSeqNo + m_iEndOff VALUE) VALUE; HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE << " maxD=" << m_iMaxPosOff VALUE - << " base=%" << m_iStartSeqNo VALUE + << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); return (m_iEndOff < m_iMaxPosOff); @@ -1096,10 +1096,10 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const int seqno_last = CSeqNo::incseq(m_iStartSeqNo VALUE, nonread_off VALUE); + const int seqno_last = CSeqNo::incseq(m_iStartSeqNo.val(), nonread_off VALUE); //const int nonread_off = (m_iFirstNonreadPos - m_iStartPos) VALUE; //const int seqno_last = (m_iStartSeqNo + nonread_off) VALUE; - return std::pair(m_iStartSeqNo VALUE, seqno_last); + return std::pair(m_iStartSeqNo.val(), seqno_last); } bool CRcvBuffer::isRcvDataReady(time_point time_now) const @@ -1539,7 +1539,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& { stringstream ss; - ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo VALUE + ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1632,7 +1632,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // If the offset is exactly at m_iEndOff, then // m_iDropOff will mark the end of gap. if (m_iDropOff) - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, m_iDropOff); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), m_iDropOff); else { LOGC(rbuflog.Error, log << "getFirstLossSeq: IPE: drop-off=0 while seq-off == end-off != max-off"); @@ -1653,7 +1653,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { - ret_seq = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); loss_off = off; break; } @@ -1675,7 +1675,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) CPos ipos ((m_iStartPos VALUE + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { - *pw_end = CSeqNo::incseq(m_iStartSeqNo VALUE, off); + *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); return ret_seq; } } diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 230efa481..c843abcec 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -17,7 +17,7 @@ #include "tsbpd_time.h" #include "utilities.h" -#define USE_WRAPPERS 1 +#define USE_WRAPPERS 0 #define USE_OPERATORS 0 namespace srt @@ -25,6 +25,9 @@ namespace srt // DEVELOPMENT TOOL - TO BE MOVED ELSEWHERE (like common.h) +// NOTE: This below series of definitions for CPos and COff +// are here for development support only, but they are not in +// use in the release code - there CPos and COff are aliases to int. #if USE_WRAPPERS struct CPos { @@ -545,11 +548,11 @@ class CRcvBuffer public: /// Get the starting position of the buffer as a packet sequence number. - int getStartSeqNo() const { return m_iStartSeqNo VALUE; } + int32_t getStartSeqNo() const { return m_iStartSeqNo.val(); } /// Sets the start seqno of the buffer. /// Must be used with caution and only when the buffer is empty. - void setStartSeqNo(int seqno) { m_iStartSeqNo = CSeqNo(seqno); } + void setStartSeqNo(int32_t seqno) { m_iStartSeqNo = CSeqNo(seqno); } /// Given the sequence number of the first unacknowledged packet /// tells the size of the buffer available for packets. @@ -561,16 +564,16 @@ class CRcvBuffer // Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo // then it does not have acknowledged packets and its full capacity is available. // Otherwise subtract the number of acknowledged but not yet read packets from its capacity. - const CSeqNo iRBufSeqNo = m_iStartSeqNo; - //if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo - if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) + const int32_t iRBufSeqNo = m_iStartSeqNo.val(); + if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo + //if (iRBufSeqNo >= CSeqNo(iFirstUnackSeqNo)) { // Full capacity is available. return capacity(); } // Note: CSeqNo::seqlen(n, n) returns 1. - return capacity() - CSeqNo::seqlen(iRBufSeqNo VALUE, iFirstUnackSeqNo) + 1; + return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1; } /// @brief Checks if the buffer has packets available for reading regardless of the TSBPD. diff --git a/srtcore/common.h b/srtcore/common.h index 1c15dd01a..e0d7212cc 100644 --- a/srtcore/common.h +++ b/srtcore/common.h @@ -685,14 +685,20 @@ class CSeqNo inline static int32_t incseq(int32_t seq) {return (seq == m_iMaxSeqNo) ? 0 : seq + 1;} + CSeqNo inc() const { return CSeqNo(incseq(value)); } + inline static int32_t decseq(int32_t seq) {return (seq == 0) ? m_iMaxSeqNo : seq - 1;} + CSeqNo dec() const { return CSeqNo(decseq(value)); } + inline static int32_t incseq(int32_t seq, int32_t inc) {return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;} // m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo // if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1) + CSeqNo inc(int32_t i) const { return CSeqNo(incseq(value, i)); } + inline static int32_t decseq(int32_t seq, int32_t dec) { // Check if seq - dec < 0, but before it would have happened @@ -705,6 +711,8 @@ class CSeqNo return seq - dec; } + CSeqNo dec(int32_t i) const { return CSeqNo(decseq(value, i)); } + static int32_t maxseq(int32_t seq1, int32_t seq2) { if (seqcmp(seq1, seq2) < 0) diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 9436fa77d..155dd7bdd 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -702,7 +702,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) array buff; SRT_MSGCTRL mc; pair seqrange; - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*5); EXPECT_EQ(mc.msgno, 2); EXPECT_EQ(seqrange, make_pair(m_init_seqno+1, m_init_seqno+5)); @@ -711,7 +711,7 @@ TEST_F(CRcvBufferReadMsg, MsgOrderScraps) EXPECT_EQ(ii.first_seq.val(), m_init_seqno+11); // 7 - EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz); + EXPECT_TRUE(rcv_buffer.readMessage(buff.data(), buff.size(), (&mc), (&seqrange)) == m_payload_sz*3); EXPECT_EQ(mc.msgno, 4); EXPECT_EQ(seqrange, make_pair(m_init_seqno+11, m_init_seqno+13)); From fa70fdaf0ee32ee8ae42a9c48d4c8cbe2c2afc24 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 12:55:09 +0200 Subject: [PATCH 142/517] Merged changes --- srtcore/buffer_rcv.cpp | 15 +++++++++++++++ srtcore/buffer_rcv.h | 12 ++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index a323360bb..d790345a6 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -435,6 +435,20 @@ void CRcvBuffer::updateGapInfo() m_iDropOff = 0; if (m_iEndOff < m_iMaxPosOff) { + CPos start = incPos(m_iStartPos, m_iEndOff + 1), + end = incPos(m_iStartPos, m_iEndOff); + + for (CPos i = start; i != end; i = incPos(i)) + { + if (m_entries[i].status == EntryState_Avail) + { + m_iDropOff = offPos(m_iStartPos, i); + break; + } + } + + /* OPTIMIZED, but buggy. + int maxend = m_szSize - m_iStartPos VALUE; int ifrom = m_iEndOff + 1; int ito = m_iMaxPosOff VALUE; @@ -463,6 +477,7 @@ void CRcvBuffer::updateGapInfo() } } } + */ // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 7ad000b69..325245194 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -208,14 +208,14 @@ const int CPos_TRAP = -1; // NOTE: in scrap region, the first cell is empty and the last one filled. // SPARE REGION: Region without packets // -// | BUSY REGION | -// | | | | -// | ICR | SCRAP REGION | SPARE REGION...-> -// ......->| | | | -// | FIRST-GAP | | +// | BUSY REGION | +// | | | | +// | ICR | SCRAP REGION | SPARE REGION...-> +// ......->| | | | +// | /FIRST-GAP | | // |<------------------- m_szSize ---------------------------->| // | |<------------ m_iMaxPosOff ----------->| | -// | | | | | | +// | | | | | | // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[] // +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+ From 1bf93ff4afc1843c8716d159ebe1b4f8a0cbe7a4 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 18 Jun 2024 15:33:09 +0200 Subject: [PATCH 143/517] Cleanup of the commented-out code --- srtcore/buffer_rcv.cpp | 56 +++++------------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index d790345a6..9e19884b7 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -148,7 +148,6 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) { SRT_ASSERT(unit != NULL); const int32_t seqno = unit->m_Packet.getSeqNo(); - //const int offset = CSeqNo::seqoff(m_iStartSeqNo, seqno); const COff offset = COff(CSeqNo(seqno) - m_iStartSeqNo); IF_RCVBUF_DEBUG(ScopedLog scoped_log); @@ -387,7 +386,7 @@ CRcvBuffer::time_point CRcvBuffer::updatePosInfo(const CUnit* unit, const COff p // position and the m_iDropOff should be calculated since that position again. void CRcvBuffer::updateGapInfo() { - COff from = m_iEndOff; //, to = m_iMaxPosOff; + COff from = m_iEndOff; SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iMaxPosOff)].status == EntryState_Empty); CPos pos = incPos(m_iStartPos, from); @@ -407,7 +406,7 @@ void CRcvBuffer::updateGapInfo() // XXX This should be this way, but there are still inconsistencies // in the message code. - //SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); + //USE: SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status == EntryState_Empty); SRT_ASSERT(m_entries[incPos(m_iStartPos, m_iEndOff)].status != EntryState_Avail); // XXX Controversy: m_iDropOff is only used in case when SRTO_TLPKTDROP @@ -447,38 +446,6 @@ void CRcvBuffer::updateGapInfo() } } - /* OPTIMIZED, but buggy. - - int maxend = m_szSize - m_iStartPos VALUE; - int ifrom = m_iEndOff + 1; - int ito = m_iMaxPosOff VALUE; - - bool found = false; - for (int i = ifrom; i < std::min(maxend, ito); ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - - if (!found && ito > maxend) - { - int upto = ito - maxend; - for (int i = 0; i < upto; ++i) - { - if (m_entries[CPos(i)].status == EntryState_Avail) - { - m_iDropOff = i; - found = true; - break; - } - } - } - */ - // Must be found somewhere, worst case at the position // of m_iMaxPosOff-1. If no finding loop caught it somehow, // it will remain at 0. The case when you have empty packets @@ -566,8 +533,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro << m_iStartSeqNo); // Drop by packet seqno range to also wipe those packets that do not exist in the buffer. - //const int offset_a = CSeqNo::seqoff(m_iStartSeqNo, seqnolo); - //const int offset_b = CSeqNo::seqoff(m_iStartSeqNo, seqnohi); const int offset_a = CSeqNo(seqnolo) - m_iStartSeqNo; const int offset_b = CSeqNo(seqnohi) - m_iStartSeqNo; if (offset_b < 0) @@ -624,7 +589,6 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro m_entries[i].status = EntryState_Drop; if (minDroppedOffset == -1) minDroppedOffset = offPos(m_iStartPos, i); - //minDroppedOffset = i - m_iStartPos; } if (end_off > m_iMaxPosOff) @@ -812,7 +776,6 @@ int CRcvBuffer::readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl, pair= 2, then probably there is a long gap, and buffer needs to be reset. - SRT_ASSERT((m_iStartPos VALUE + offset VALUE) / m_szSize < 2); + SRT_ASSERT((m_iStartPos + offset) / m_szSize < 2); - //const CPos newpktpos = m_iStartPos + offset; const CPos newpktpos = incPos(m_iStartPos, offset); const COff prev_max_off = m_iMaxPosOff; bool extended_end = false; @@ -193,7 +192,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) // possible even before checking that the packet // exists because existence of a packet beyond // the current max position is not possible). - SRT_ASSERT(newpktpos VALUE >= 0 && newpktpos VALUE < int(m_szSize)); + SRT_ASSERT(newpktpos >= 0 && newpktpos < int(m_szSize)); if (m_entries[newpktpos].status != EntryState_Empty) { IF_RCVBUF_DEBUG(scoped_log.ss << " returns -1"); @@ -466,7 +465,6 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); COff len = COff(CSeqNo(seqno) - m_iStartSeqNo); - //int len = CSeqNo::seqoff(m_iStartSeqNo, seqno); if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); @@ -479,7 +477,7 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) int iNumDropped = 0; // Number of dropped packets that were missing. int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. - while (len VALUE > 0) + while (len > 0) { // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. @@ -685,10 +683,10 @@ bool CRcvBuffer::getContiguousEnd(int32_t& w_seq) const return m_iMaxPosOff > 0; } - w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff VALUE); + w_seq = CSeqNo::incseq(m_iStartSeqNo.val(), m_iEndOff); - HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff VALUE - << " maxD=" << m_iMaxPosOff VALUE + HLOGC(rbuflog.Debug, log << "CONTIG: endD=" << m_iEndOff + << " maxD=" << m_iMaxPosOff << " base=%" << m_iStartSeqNo.val() << " end=%" << w_seq); @@ -917,7 +915,7 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) m_iStartPos = p; --m_iMaxPosOff; - SRT_ASSERT(m_iMaxPosOff VALUE >= 0); + SRT_ASSERT(m_iMaxPosOff >= 0); m_iEndOff = decOff(m_iEndOff, 1); m_iDropOff = decOff(m_iDropOff, 1); @@ -943,8 +941,8 @@ int CRcvBuffer::readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg) if (iBytesRead == 0) { - LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos VALUE - << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos VALUE); + LOGC(rbuflog.Error, log << "readBufferTo: 0 bytes read. m_iStartPos=" << m_iStartPos + << ", m_iFirstNonreadPos=" << m_iFirstNonreadPos); } IF_HEAVY_LOGGING(debugShowState("readbuf")); @@ -968,7 +966,7 @@ bool CRcvBuffer::hasAvailablePackets() const int CRcvBuffer::getRcvDataSize() const { - return offPos(m_iStartPos, m_iFirstNonreadPos) VALUE; + return offPos(m_iStartPos, m_iFirstNonreadPos); } int CRcvBuffer::getTimespan_ms() const @@ -1057,7 +1055,7 @@ CRcvBuffer::PacketInfo CRcvBuffer::getFirstValidPacketInfo() const std::pair CRcvBuffer::getAvailablePacketsRange() const { const COff nonread_off = offPos(m_iStartPos, m_iFirstNonreadPos); - const CSeqNo seqno_last = m_iStartSeqNo + nonread_off VALUE; + const CSeqNo seqno_last = m_iStartSeqNo + nonread_off; return std::pair(m_iStartSeqNo.val(), seqno_last.val()); } @@ -1488,7 +1486,7 @@ string CRcvBuffer::strFullnessState(int32_t iFirstUnackSeqNo, const time_point& stringstream ss; ss << "iFirstUnackSeqNo=" << iFirstUnackSeqNo << " m_iStartSeqNo=" << m_iStartSeqNo.val() - << " m_iStartPos=" << m_iStartPos VALUE << " m_iMaxPosOff=" << m_iMaxPosOff VALUE << ". "; + << " m_iStartPos=" << m_iStartPos << " m_iMaxPosOff=" << m_iMaxPosOff << ". "; ss << "Space avail " << getAvailSize(iFirstUnackSeqNo) << "/" << m_szSize << " pkts. "; @@ -1555,17 +1553,15 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) if (m_iEndOff == m_iMaxPosOff) return SRT_SEQNO_NONE; - //int offset = CSeqNo::seqoff(m_iStartSeqNo, fromseq); - int offset_val = CSeqNo(fromseq) - m_iStartSeqNo; - COff offset (offset_val); + COff offset = COff(CSeqNo(fromseq) - m_iStartSeqNo); // Check if it's still inside the buffer. // Skip the region from 0 to m_iEndOff because this // region is by definition contiguous and contains no loss. - if (offset_val < m_iEndOff || offset >= m_iMaxPosOff) + if (offset < m_iEndOff || offset >= m_iMaxPosOff) { - HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset VALUE << " for %" << fromseq - << " (with max=" << m_iMaxPosOff VALUE << ") - NO LOSS FOUND"); + HLOGC(rbuflog.Debug, log << "getFirstLossSeq: offset=" << offset << " for %" << fromseq + << " (with max=" << m_iMaxPosOff << ") - NO LOSS FOUND"); return SRT_SEQNO_NONE; } @@ -1597,7 +1593,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) // find it earlier by checking packet presence. for (int off = offset; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status == EntryState_Empty) { ret_seq = CSeqNo::incseq(m_iStartSeqNo.val(), off); @@ -1619,7 +1615,7 @@ int32_t CRcvBuffer::getFirstLossSeq(int32_t fromseq, int32_t* pw_end) { for (int off = loss_off+1; off < m_iMaxPosOff; ++off) { - CPos ipos ((m_iStartPos VALUE + off) % m_szSize); + CPos ipos ((m_iStartPos + off) % m_szSize); if (m_entries[ipos].status != EntryState_Empty) { *pw_end = CSeqNo::incseq(m_iStartSeqNo.val(), off); diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index 325245194..ec5f2c256 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -148,8 +148,6 @@ struct COff operator bool() const { return value != 0; } }; -#define VALUE .val() - #if USE_OPERATORS inline CPos operator+(const CPos& pos, COff off) @@ -194,7 +192,6 @@ inline CSeqNo operator-(CSeqNo seq, COff off) const CPos CPos_TRAP (-1); #else -#define VALUE typedef int CPos; typedef int COff; const int CPos_TRAP = -1; @@ -679,11 +676,11 @@ class CRcvBuffer private: //* - CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos VALUE + inc VALUE) % m_szSize); } - CPos decPos(CPos pos) const { return (pos VALUE - 1) >= 0 ? CPos(pos VALUE - 1) : CPos(m_szSize - 1); } + CPos incPos(CPos pos, COff inc = COff(1)) const { return CPos((pos + inc) % m_szSize); } + CPos decPos(CPos pos) const { return (pos - 1) >= 0 ? CPos(pos - 1) : CPos(m_szSize - 1); } COff offPos(CPos pos1, CPos pos2) const { - int diff = pos2 VALUE - pos1 VALUE; + int diff = pos2 - pos1; if (diff >= 0) { return COff(diff); @@ -695,7 +692,7 @@ class CRcvBuffer static COff decOff(COff val, int shift) { - int ival = val VALUE - shift; + int ival = val - shift; if (ival < 0) return COff(0); return COff(ival); @@ -722,14 +719,13 @@ class CRcvBuffer if (iFirstNonreadPos == iStartPos) return true; - const CPos iLastPos = CPos((iStartPos VALUE + iMaxPosOff VALUE) % int(iSize)); - //const CPos iLastPos = iStartPos + iMaxPosOff; - const bool isOverrun = iLastPos VALUE < iStartPos VALUE; + const CPos iLastPos = CPos((iStartPos + iMaxPosOff) % int(iSize)); + const bool isOverrun = iLastPos < iStartPos; if (isOverrun) - return iFirstNonreadPos VALUE > iStartPos VALUE || iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos || iFirstNonreadPos <= iLastPos; - return iFirstNonreadPos VALUE > iStartPos VALUE && iFirstNonreadPos VALUE <= iLastPos VALUE; + return iFirstNonreadPos > iStartPos && iFirstNonreadPos <= iLastPos; } bool isInUsedRange(CPos iFirstNonreadPos) @@ -738,11 +734,11 @@ class CRcvBuffer return true; // DECODE the iFirstNonreadPos - int diff = iFirstNonreadPos VALUE - m_iStartPos VALUE; + int diff = iFirstNonreadPos - m_iStartPos; if (diff < 0) diff += m_szSize; - return diff <= m_iMaxPosOff VALUE; + return diff <= m_iMaxPosOff; } // NOTE: Assumes that pUnit != NULL From 0a607c7c30da6aa35291a36b877c6445c711cdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 18 Jun 2024 18:38:05 +0200 Subject: [PATCH 145/517] Some cosmetic fixes. Fixed the use of std::abs --- srtcore/core.cpp | 24 +++++++++++++----------- srtcore/logging.h | 6 +++--- srtcore/sfmt.h | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f87692caa..456d17ab1 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -89,11 +89,12 @@ using namespace srt; using namespace srt::sync; using namespace srt_logging; using fmt::sfmt; +using fmt::sfmc; const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; -static const char onoff[2] = {'-', '+'}; +static inline char fmt_onoff(bool val) { return val ? '+' : '-'; } //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ @@ -1777,10 +1778,10 @@ bool srt::CUDT::createSrtHandshake( LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." << " Socket state: " - << onoff[m_bConnected] << "connected, " - << onoff[m_bConnecting] << "connecting, " - << onoff[m_bBroken] << "broken, " - << onoff[m_bClosing] << "closing."); + << fmt_onoff(m_bConnected) << "connected, " + << fmt_onoff(m_bConnecting) << "connecting, " + << fmt_onoff(m_bBroken) << "broken, " + << fmt_onoff(m_bClosing) << "closing."); return false; } @@ -4109,11 +4110,11 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas LOGC(cnlog.Error, log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." << " Socket state: " - << onoff[m_bConnected] << "connected, " - << onoff[m_bConnecting] << "connecting, " - << onoff[m_bBroken] << "broken, " - << onoff[m_bOpened] << "opened, " - << onoff[m_bClosing] << "closing."); + << fmt_onoff(m_bConnected) << "connected, " + << fmt_onoff(m_bConnecting) << "connecting, " + << fmt_onoff(m_bBroken) << "broken, " + << fmt_onoff(m_bOpened) << "opened, " + << fmt_onoff(m_bClosing) << "closing."); return CONN_REJECT; } // This is a periodic handshake update, so you need to extract the KM data from the @@ -7777,7 +7778,8 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) - << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" << sfmt(cgwindow, ".3")); + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + << sfmt(cgwindow, sfmc().precision(3))); #endif } diff --git a/srtcore/logging.h b/srtcore/logging.h index 94cda07be..c7ff11704 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -342,7 +342,7 @@ struct LogDispatcher::Proxy template Proxy& operator<<(const srt::sync::atomic& arg) { - if ( that_enabled ) + if (that_enabled) { os << arg.load(); } @@ -376,9 +376,9 @@ struct LogDispatcher::Proxy ~Proxy() { - if ( that_enabled ) + if (that_enabled) { - if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 ) + if ((flags & SRT_LOGF_DISABLE_EOL) == 0) os << fmt::seol; that.SendLogLine(i_file, i_line, area, os.str()); } diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 1db88bba7..b6eecf451 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -430,8 +430,8 @@ struct sfmc SFMTC_TAG(alt, altbit = true); SFMTC_TAG(left, leftbit = true); SFMTC_TAG(right, (void)0); - SFMTC_TAG_VAL(width, widthbit = true; widthval = abs(val)); - SFMTC_TAG_VAL(precision, precisionbit = true; precisionval = abs(val)); + SFMTC_TAG_VAL(width, widthbit = true; widthval = std::abs(val)); + SFMTC_TAG_VAL(precision, precisionbit = true; precisionval = std::abs(val)); SFMTC_TAG(dec, (void)0); SFMTC_TAG(hex, presentation = flavor_hex); SFMTC_TAG(oct, presentation = flavor_oct); From d2ec1cf653a0800decc167c538c7f2d40c2caf97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 19 Jun 2024 09:12:30 +0200 Subject: [PATCH 146/517] Fixed usage of with std --- srtcore/sfmt.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index b6eecf451..715e7aa96 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -260,8 +260,8 @@ form_memory_buffer<> fix_format(const char* fmt, // All these arrays must contain at least 2 elements, // that is one character and terminating zero. //Ensure= 2> c1; - Ensure= 2> c2; (void)c2; - Ensure= 2> c3; (void)c3; + Ensure= 2)> c2; (void)c2; + Ensure= 2)> c3; (void)c3; form_memory_buffer<> buf; buf.append('%'); @@ -311,7 +311,7 @@ form_memory_buffer<> fix_format(const char* fmt, inline form_memory_buffer<> apply_format_fix(TYPE, const char* fmt) \ { \ return fix_format(fmt, ALLOWED, TYPED, DEFTYPE, WARN); \ -} +} #define SFMT_FORMAT_FIXER_TPL(TPAR, TYPE, ALLOWED, TYPED, DEFTYPE, WARN) \ template\ @@ -456,10 +456,10 @@ struct sfmc // Utility function to store the number for width/precision // For C++11 it could be constexpr, but this is C++03-compat code. // It's bound to this structure because it's unsafe. - static size_t store_number(char* position, int number) + static std::size_t store_number(char* position, int number) { - size_t shiftpos = 0; - div_t dm = div(number, 10); + std::size_t shiftpos = 0; + std::div_t dm = std::div(number, 10); if (dm.quot) shiftpos = store_number(position, dm.quot); position[shiftpos] = '0' + dm.rem; @@ -471,7 +471,7 @@ struct sfmc { using namespace internal; - Ensure= 2> c3; (void)c3; + Ensure= 2)> c3; (void)c3; form_memory_buffer<> form; From 193fe39d3010ecf8eb2b3209eee5f8129220eaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Wed, 19 Jun 2024 09:52:06 +0200 Subject: [PATCH 147/517] Fixed correct includes for std::div --- srtcore/sfmt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/srtcore/sfmt.h b/srtcore/sfmt.h index 715e7aa96..fc653d757 100644 --- a/srtcore/sfmt.h +++ b/srtcore/sfmt.h @@ -15,7 +15,8 @@ #include #include -#include +#include // std::abs +#include // std::div #include #include #include From c81d4d609757fc3bce37d7b751f8de4a21441cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 20 Jun 2024 15:05:25 +0200 Subject: [PATCH 148/517] Renamed sfmt.h and moved to srt namespace --- apps/logsupport.cpp | 2 +- apps/uriparser.cpp | 2 +- srtcore/api.cpp | 4 +-- srtcore/common.cpp | 6 ++-- srtcore/congctl.cpp | 2 +- srtcore/core.cpp | 6 ++-- srtcore/logging.cpp | 4 +-- srtcore/logging.h | 14 ++++---- srtcore/queue.cpp | 2 +- srtcore/socketconfig.cpp | 2 -- srtcore/{sfmt.h => srt_sfmt.h} | 11 +++++- srtcore/sync.cpp | 16 ++++----- srtcore/sync.h | 6 ++-- srtcore/utilities.h | 62 ++++++++-------------------------- 14 files changed, 55 insertions(+), 84 deletions(-) rename srtcore/{sfmt.h => srt_sfmt.h} (95%) diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index 7192f4596..86deb1690 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -173,7 +173,7 @@ set SrtParseLogFA(string fa, set* punknown) void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) { - fmt::obufstream son, soff; + srt::obufstream son, soff; for (auto& s: speclist) { diff --git a/apps/uriparser.cpp b/apps/uriparser.cpp index 47e8f6a7f..7425bca03 100644 --- a/apps/uriparser.cpp +++ b/apps/uriparser.cpp @@ -64,7 +64,7 @@ string UriParser::makeUri() prefix = m_proto + "://"; } - fmt::obufstream out; + srt::obufstream out; out << prefix << m_host; if ((m_port == "" || m_port == "0") && m_expect == EXPECT_FILE) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 93ad23725..b436bda1b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -231,7 +231,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) if (sock == 0) return ""; - fmt::obufstream os; + srt::obufstream os; os << "@" << sock << ":"; return os.str(); } @@ -3249,7 +3249,7 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer& m = i->second; #if ENABLE_HEAVY_LOGGING - fmt::obufstream that_muxer; + srt::obufstream that_muxer; that_muxer << "id=" << m.m_iID << " port=" << m.m_iPort << " ip=" << (m.m_iIPversion == AF_INET ? "v4" : "v6"); #endif diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 843420354..5bc849248 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -277,10 +277,10 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc } else { - fmt::obufstream peeraddr_form; - peeraddr_form << fmt::sfmt(peeraddr16[0], "04x"); + obufstream peeraddr_form; + peeraddr_form << sfmt(peeraddr16[0], "04x"); for (int i = 1; i < 8; ++i) - peeraddr_form << ":" << fmt::sfmt(peeraddr16[i], "04x"); + peeraddr_form << ":" << sfmt(peeraddr16[i], "04x"); LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form); *target_ipv4_addr = 0; diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 0f6edb15b..cd7d126f6 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -595,7 +595,7 @@ class FileCC : public SrtCongestionControlBase { m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << fmt::sfmt(m_dCWndSize, ".6") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); + << sfmt(m_dCWndSize, ".6") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 456d17ab1..701f0b0a6 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -88,8 +88,6 @@ using namespace std; using namespace srt; using namespace srt::sync; using namespace srt_logging; -using fmt::sfmt; -using fmt::sfmc; const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; @@ -10014,9 +10012,9 @@ int srt::CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - fmt::obufstream buf; + obufstream buf; // Take the last 2 ciphers from the socket ID. - string s = fmt::sfmts(m_SocketID, "02"); + string s = sfmts(m_SocketID, "02"); buf << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); const string thname = buf.str(); #else diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index 8ccfa30f1..e76e6db9a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -43,7 +43,7 @@ LogDispatcher::Proxy LogDispatcher::operator()() return Proxy(*this); } -void LogDispatcher::CreateLogLinePrefix(fmt::obufstream& serr) +void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) { using namespace std; using namespace srt; @@ -60,7 +60,7 @@ void LogDispatcher::CreateLogLinePrefix(fmt::obufstream& serr) if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) { - serr << tmp_buf << fmt::sfmt(tv.tv_usec, "06"); + serr << tmp_buf << srt::sfmt(tv.tv_usec, "06"); } } diff --git a/srtcore/logging.h b/srtcore/logging.h index c7ff11704..cefa5b7ce 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -28,7 +28,7 @@ written by #include #endif -#include "sfmt.h" +#include "srt_sfmt.h" #include "srt.h" #include "utilities.h" @@ -193,7 +193,7 @@ struct SRT_API LogDispatcher bool CheckEnabled(); - void CreateLogLinePrefix(fmt::obufstream&); + void CreateLogLinePrefix(srt::obufstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); // log.Debug("This is the ", nth, " time"); <--- C++11 only. @@ -286,7 +286,7 @@ struct LogDispatcher::Proxy { LogDispatcher& that; - fmt::obufstream os; + srt::obufstream os; // Cache the 'enabled' state in the beginning. If the logging // becomes enabled or disabled in the middle of the log, we don't @@ -379,7 +379,7 @@ struct LogDispatcher::Proxy if (that_enabled) { if ((flags & SRT_LOGF_DISABLE_EOL) == 0) - os << fmt::seol; + os << srt::seol; that.SendLogLine(i_file, i_line, area, os.str()); } // Needed in destructor? @@ -473,10 +473,10 @@ inline bool LogDispatcher::CheckEnabled() //extern std::mutex Debug_mutex; -inline void PrintArgs(fmt::obufstream&) {} +inline void PrintArgs(srt::obufstream&) {} template -inline void PrintArgs(fmt::obufstream& serr, Arg1&& arg1, Args&&... args) +inline void PrintArgs(srt::obufstream& serr, Arg1&& arg1, Args&&... args) { serr << std::forward(arg1); PrintArgs(serr, args...); @@ -484,7 +484,7 @@ inline void PrintArgs(fmt::obufstream& serr, Arg1&& arg1, Args&&... args) // Add exceptional handling for sync::atomic template -inline void PrintArgs(fmt::obufstream& serr, const srt::sync::atomic& arg1, Args&&... args) +inline void PrintArgs(srt::obufstream& serr, const srt::sync::atomic& arg1, Args&&... args) { serr << arg1.load(); PrintArgs(serr, args...); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 78de6a6b9..43f7349fc 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1079,7 +1079,7 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, { HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << fmt::sfmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, "f") << "s of TTL)..."); + << sfmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, "f") << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index cec4845b4..6d1acf754 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -52,8 +52,6 @@ written by #include "srt.h" #include "socketconfig.h" -using fmt::sfmt; - namespace srt { int RcvBufferSizeOptionToValue(int val, int flightflag, int mss) diff --git a/srtcore/sfmt.h b/srtcore/srt_sfmt.h similarity index 95% rename from srtcore/sfmt.h rename to srtcore/srt_sfmt.h index fc653d757..e11b0b730 100644 --- a/srtcore/sfmt.h +++ b/srtcore/srt_sfmt.h @@ -1,3 +1,12 @@ +// SOURCE NOTE: +// +// This is copied directly from a public-domain library: +// +// URI: https://github.com/ethouris/fmt +// PATH: include/fmt/sfmt.h +// +// with slight modifications. +// // Formatting library for C++ - C++03 compat version of on-demand tagged format API. // // Copyright (c) 2024 - present, Mikołaj Małecki @@ -21,7 +30,7 @@ #include #include -namespace fmt +namespace srt { namespace internal diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 9dcaabb54..ff7473ca3 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -51,15 +51,15 @@ std::string FormatTime(const steady_clock::time_point& timestamp) const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; steady_clock::time_point frac = timestamp - seconds_from(total_sec); - fmt::obufstream out; + srt::obufstream out; if (days) out << days << "D "; - out << fmt::sfmt(hours, "02") << ":" - << fmt::sfmt(minutes, "02") << ":" - << fmt::sfmt(seconds, "02") << "." - << fmt::sfmt(frac.time_since_epoch().count(), - fmt::sfmc().fillzero().width(decimals)) + out << srt::sfmt(hours, "02") << ":" + << srt::sfmt(minutes, "02") << ":" + << srt::sfmt(seconds, "02") << "." + << srt::sfmt(frac.time_since_epoch().count(), + srt::sfmc().fillzero().width(decimals)) << " [STDY]"; return out.str(); } @@ -76,8 +76,8 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) char tmp_buf[512]; strftime(tmp_buf, 512, "%X.", &tm); - fmt::obufstream out; - out << tmp_buf << fmt::sfmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, "06") << " [SYST]"; + srt::obufstream out; + out << tmp_buf << srt::sfmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, "06") << " [SYST]"; return out.str(); } diff --git a/srtcore/sync.h b/srtcore/sync.h index 627f7eac5..40584a838 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -55,7 +55,7 @@ #include "srt.h" #include "utilities.h" #include "srt_attr_defs.h" -#include "sfmt.h" +#include "srt_sfmt.h" namespace srt { @@ -775,8 +775,8 @@ struct DurationUnitName template inline std::string FormatDuration(const steady_clock::duration& dur) { - fmt::obufstream out; - out << fmt::sfmt(DurationUnitName::count(dur), "f") << DurationUnitName::name(); + obufstream out; + out << sfmt(DurationUnitName::count(dur), "f") << DurationUnitName::name(); return out.str(); } diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 83405b44c..d7f353765 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -45,7 +45,7 @@ written by #include #include -#include "sfmt.h" +#include "srt_sfmt.h" // -------------- UTILITIES ------------------------ @@ -485,7 +485,7 @@ class FixedArray void throw_invalid_index(int i) const { - fmt::obufstream ss; + srt::obufstream ss; ss << "Index " << i << "out of range"; throw std::runtime_error(ss.str()); } @@ -587,7 +587,7 @@ inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) template inline std::string Sprint(Args&&... args) { - fmt::obufstream sout; + srt::obufstream sout; Print(sout, args...); return sout.str(); } @@ -673,14 +673,14 @@ class UniquePtr: public std::auto_ptr template inline std::string Sprint(const Arg1& arg) { - return fmt::sfmts(arg); + return srt::sfmts(arg); } // Ok, let it be 2-arg, in case when a manipulator is needed template inline std::string Sprint(const Arg1& arg1, const Arg2& arg2) { - fmt::obufstream sout; + srt::obufstream sout; sout << arg1 << arg2; return sout.str(); } @@ -718,11 +718,11 @@ typename Map::mapped_type const* map_getp(const Map& m, const Key& key) template inline std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) { - fmt::obufstream os; + srt::obufstream os; os << "[ "; typedef typename Container::const_iterator it_t; for (it_t i = in.begin(); i != in.end(); ++i) - os << fmt::sfmt(*i, fmt) << " "; + os << srt::sfmt(*i, fmt) << " "; os << "]"; return os.str(); } @@ -732,11 +732,11 @@ template inline std::string Printable(const Container& in, std::pair/*pseudoargument*/, const char* fmtk = 0, const char* fmtv = 0) { using namespace srt_pair_op; - fmt::obufstream os; + srt::obufstream os; os << "[ "; typedef typename Container::const_iterator it_t; for (it_t i = in.begin(); i != in.end(); ++i) - os << fmt::sfmt(i->first, fmtk) << ":" << fmt::sfmt(i->second, fmtv) << " "; + os << srt::sfmt(i->first, fmtk) << ":" << srt::sfmt(i->second, fmtv) << " "; os << "]"; return os.str(); } @@ -757,7 +757,7 @@ std::string PrintableMod(const Container& in, const std::string& prefix) { using namespace srt_pair_op; typedef typename Container::value_type Value; - fmt::obufstream os; + srt::obufstream os; os << "[ "; for (typename Container::const_iterator y = in.begin(); y != in.end(); ++y) os << prefix << Value(*y) << " "; @@ -980,47 +980,13 @@ inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) if ( size == 0 ) return ""; - using namespace fmt; + srt::obufstream os; - obufstream os; - - os << sfmt(bytes[0], "02X"); - for (size_t i = 1; i < size; ++i) - { - os << sfmt(bytes[i], "02X"); - } - return os.str(); - - /* OLD VERSION - - //char buf[256]; - using namespace std; - - ostringstream os; - - // I know, it's funny to use sprintf and ostringstream simultaneously, - // but " %02X" in iostream is: << " " << hex << uppercase << setw(2) << setfill('0') << VALUE << setw(1) - // Too noisy. OTOH ostringstream solves the problem of memory allocation - // for a string of unpredictable size. - //sprintf(buf, "%02X", int(bytes[0])); - - os.fill('0'); - os.width(2); - os.setf(ios::basefield, ios::hex); - os.setf(ios::uppercase); - - //os << buf; - os << int(bytes[0]); - - - for (size_t i = 1; i < size; ++i) + for (size_t i = 0; i < size; ++i) { - //sprintf(buf, " %02X", int(bytes[i])); - //os << buf; - os << int(bytes[i]); + os << srt::sfmt(bytes[i], "02X"); } return os.str(); - */ } @@ -1184,7 +1150,7 @@ inline std::string BufferStamp(const char* mem, size_t size) } // Convert to hex string - return fmt::sfmts(sum, "08X"); + return srt::sfmts(sum, "08X"); } template From 0920fbf7f3d2e2b380e0d5d90e7729354e586f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 25 Jun 2024 12:08:03 +0200 Subject: [PATCH 149/517] [core] Setting up fmt with iomanip for logging system --- srtcore/api.cpp | 2 +- srtcore/common.cpp | 4 +- srtcore/congctl.cpp | 2 +- srtcore/core.cpp | 14 +++---- srtcore/logging.cpp | 22 +++++------ srtcore/logging.h | 46 ++++++++++++----------- srtcore/queue.cpp | 2 +- srtcore/socketconfig.cpp | 8 ++-- srtcore/srt_sfmt.h | 6 +++ srtcore/sync.cpp | 14 ++++--- srtcore/utilities.h | 80 +++++++++++++++++++++++++++++++++------- 11 files changed, 131 insertions(+), 69 deletions(-) diff --git a/srtcore/api.cpp b/srtcore/api.cpp index b436bda1b..840ef3acd 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -1405,7 +1405,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 = "group-derived option: #" + Sprint(g.m_config[i].so); + error_reason = Sprint("group-derived option: #", 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/common.cpp b/srtcore/common.cpp index 5bc849248..acef9ee77 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -278,9 +278,9 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc else { obufstream peeraddr_form; - peeraddr_form << sfmt(peeraddr16[0], "04x"); + peeraddr_form << fmt(peeraddr16[0], hex, setfill('0'), setw(4)); for (int i = 1; i < 8; ++i) - peeraddr_form << ":" << sfmt(peeraddr16[i], "04x"); + peeraddr_form << ":" << fmt(peeraddr16[i], hex, setfill('0'), setw(4)); LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form); *target_ipv4_addr = 0; diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index cd7d126f6..012d766b5 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -595,7 +595,7 @@ class FileCC : public SrtCongestionControlBase { m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << sfmt(m_dCWndSize, ".6") << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); + << fmt(m_dCWndSize, setprecision(6)) << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 701f0b0a6..9eec4a9ae 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2107,8 +2107,8 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint } LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << " vers=0x" << sfmt(srtdata[SRT_HS_VERSION], "x") - << " opts=0x" << sfmt(srtdata[SRT_HS_FLAGS], "x") + << " vers=0x" << fmt(srtdata[SRT_HS_VERSION], hex) + << " opts=0x" << fmt(srtdata[SRT_HS_FLAGS], hex) << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; @@ -2343,7 +2343,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion) - << " Flags: SND:" << sfmt(m_uPeerSrtFlags, "08x") + << " Flags: SND:" << fmt(m_uPeerSrtFlags, hex, setfill('0'), setw(8)) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); // Basic version check if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) @@ -5470,14 +5470,14 @@ void * srt::CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " - << (timediff_us / 1000) << "." << sfmt(timediff_us % 1000, "03") << " ms"); + << (timediff_us / 1000) << "." << fmt(timediff_us % 1000, fixed, setfill('0'), setw(3)) << " ms"); #endif string why; if (self->frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) { LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno << " delayed for " << (timediff_us / 1000) << "." - << sfmt(timediff_us % 1000, "03") << " ms " << why); + << fmt(timediff_us % 1000, fixed, setfill('0'), setw(3)) << " ms " << why); } #if SRT_ENABLE_FREQUENT_LOG_TRACE else @@ -7777,7 +7777,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" - << sfmt(cgwindow, sfmc().precision(3))); + << fmt(cgwindow, setprecision(3))); #endif } @@ -8418,7 +8418,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // included, but it also triggers for any other kind of invalid value. // This check MUST BE DONE before making any operation on this number. LOGC(inlog.Error, log << CONID() << "ACK: IPE/EPE: received invalid ACK value: " << ackdata_seqno - << " " << sfmt(ackdata_seqno, "x") << " (IGNORED)"); + << " " << fmt(ackdata_seqno, hex) << " (IGNORED)"); return; } diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index e76e6db9a..1c51821e2 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -51,7 +51,7 @@ void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) SRT_STATIC_ASSERT(ThreadName::BUFSIZE >= sizeof("hh:mm:ss.") * 2, // multiply 2 for some margin "ThreadName::BUFSIZE is too small to be used for strftime"); char tmp_buf[ThreadName::BUFSIZE]; - if ( !isset(SRT_LOGF_DISABLE_TIME) ) + if (!isset(SRT_LOGF_DISABLE_TIME)) { // Not necessary if sending through the queue. timeval tv; @@ -60,25 +60,23 @@ void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) { - serr << tmp_buf << srt::sfmt(tv.tv_usec, "06"); + serr << tmp_buf << fmt(tv.tv_usec, setfill('0'), setw(6)); } } - string out_prefix; - if ( !isset(SRT_LOGF_DISABLE_SEVERITY) ) - { - out_prefix = prefix; - } - // Note: ThreadName::get needs a buffer of size min. ThreadName::BUFSIZE - if ( !isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf) ) + if (!isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf)) { - serr << "/" << tmp_buf << out_prefix << ": "; + serr << "/" << tmp_buf; } - else + + if (!isset(SRT_LOGF_DISABLE_SEVERITY)) { - serr << out_prefix << ": "; + //serr << prefix; + serr.write(prefix, prefix_len); // include terminal 0 } + + serr << ": "; } std::string LogDispatcher::Proxy::ExtractName(std::string pretty_function) diff --git a/srtcore/logging.h b/srtcore/logging.h index cefa5b7ce..3e8dfd07f 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -28,7 +28,7 @@ written by #include #endif -#include "srt_sfmt.h" +//#include "srt_sfmt.h" #include "srt.h" #include "utilities.h" @@ -148,6 +148,7 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; + size_t prefix_len; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -160,30 +161,30 @@ struct SRT_API LogDispatcher level(log_level), src_config(&config) { - // XXX stpcpy desired, but not enough portable - // Composing the exact prefix is not critical, so simply - // cut the prefix, if the length is exceeded - - // See Logger::Logger; we know this has normally 2 characters, - // except !!FATAL!!, which has 9. Still less than 32. - // If the size of the FA name together with severity exceeds the size, - // just skip the former. - if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE) + size_t your_pfx_len = your_pfx ? strlen(your_pfx) : 0; + size_t logger_pfx_len = logger_pfx ? strlen(logger_pfx) : 0; + + if (logger_pfx && your_pfx_len + logger_pfx_len + 1 < MAX_PREFIX_SIZE) { -#if defined(_MSC_VER) && _MSC_VER < 1900 - _snprintf(prefix, MAX_PREFIX_SIZE, "%s:%s", your_pfx, logger_pfx); -#else - snprintf(prefix, MAX_PREFIX_SIZE + 1, "%s:%s", your_pfx, logger_pfx); -#endif + memcpy(prefix, your_pfx, your_pfx_len); + prefix[your_pfx_len] = ':'; + memcpy(prefix + your_pfx_len + 1, logger_pfx, logger_pfx_len); + prefix[your_pfx_len + logger_pfx_len + 1] = '\0'; + prefix_len = your_pfx_len + logger_pfx_len + 1; + } + else if (your_pfx) + { + // Prefix too long, so copy only your_pfx and only + // as much as it fits + size_t copylen = std::min(+MAX_PREFIX_SIZE, your_pfx_len); + memcpy(prefix, your_pfx, copylen); + prefix[copylen] = '\0'; + prefix_len = copylen; } else { -#ifdef _MSC_VER - strncpy_s(prefix, MAX_PREFIX_SIZE + 1, your_pfx, _TRUNCATE); -#else - strncpy(prefix, your_pfx, MAX_PREFIX_SIZE); - prefix[MAX_PREFIX_SIZE] = '\0'; -#endif + prefix[0] = '\0'; + prefix_len = 0; } } @@ -522,7 +523,8 @@ inline void LogDispatcher::SendLogLine(const char* file, int line, const std::st } else if ( src_config->log_stream ) { - (*src_config->log_stream) << msg; + //(*src_config->log_stream) << msg; + src_config->log_stream->write(msg.data(), msg.size()); (*src_config->log_stream).flush(); } src_config->unlock(); diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 43f7349fc..a5dfde68d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1079,7 +1079,7 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, { HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << sfmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, "f") << "s of TTL)..."); + << fmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, fixed) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 6d1acf754..955a9006d 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -744,8 +744,8 @@ struct CSrtConfigSetter { co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << sfmt(km_refresh, "x") << ": setting SRTO_KMPREANNOUNCE=0x" - << sfmt(co.uKmPreAnnouncePkt, "x")); + log << "SRTO_KMREFRESHRATE=0x" << fmt(km_refresh, std::hex) << ": setting SRTO_KMPREANNOUNCE=0x" + << fmt(co.uKmPreAnnouncePkt, std::hex)); } } }; @@ -770,8 +770,8 @@ struct CSrtConfigSetter if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << sfmt(km_preanno, "x") - << " exceeds KmRefresh/2, 0x" << sfmt((kmref - 1) / 2, "x") + log << "SRTO_KMPREANNOUNCE=0x" << fmt(km_preanno, std::hex) + << " exceeds KmRefresh/2, 0x" << fmt((kmref - 1) / 2, std::hex) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } diff --git a/srtcore/srt_sfmt.h b/srtcore/srt_sfmt.h index e11b0b730..c4c096852 100644 --- a/srtcore/srt_sfmt.h +++ b/srtcore/srt_sfmt.h @@ -797,6 +797,12 @@ class obufstream return *this; } + obufstream& write(const char* t, size_t size) + { + buffer.append(t, size); + return *this; + } + // For unusual manipulation, usually to add NUL termination. // NOTE: you must make sure that you won't use the extended // buffers if the intention was to get a string. diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index ff7473ca3..af193c603 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -55,11 +55,11 @@ std::string FormatTime(const steady_clock::time_point& timestamp) if (days) out << days << "D "; - out << srt::sfmt(hours, "02") << ":" - << srt::sfmt(minutes, "02") << ":" - << srt::sfmt(seconds, "02") << "." - << srt::sfmt(frac.time_since_epoch().count(), - srt::sfmc().fillzero().width(decimals)) + out << srt::fmt(hours, setfill('0'), setw(2)) << ":" + << srt::fmt(minutes, setfill('0'), setw(2)) << ":" + << srt::fmt(seconds, setfill('0'), setw(2)) << "." + << srt::fmt(frac.time_since_epoch().count(), + setfill('0'), setw(decimals)) << " [STDY]"; return out.str(); } @@ -77,7 +77,9 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) strftime(tmp_buf, 512, "%X.", &tm); srt::obufstream out; - out << tmp_buf << srt::sfmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, "06") << " [SYST]"; + out << tmp_buf + << srt::fmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, setfill('0'), setw(6)) + << " [SYST]"; return out.str(); } diff --git a/srtcore/utilities.h b/srtcore/utilities.h index d7f353765..f85d5b23f 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -567,6 +567,61 @@ namespace any_op } } +// XXX Consider this whole file to be namespace srt! +namespace srt +{ +class fmt_sender_proxy +{ + std::stringstream os; + +public: + + template + explicit fmt_sender_proxy(const TYPE& v, const Args&... manips) + { + manipulate(manips...); + os << v; + } + + void manipulate() {} + + template + void manipulate(const Arg1& manip, const Args&... manips) + { + os << manip; + manipulate(manips...); + } + + template + fmt_sender_proxy& operator *(const OUTER& p) + { + os << p; + return *this; + } + + template + void sendto(OUTSTR& stream) const + { + stream << os.rdbuf(); + } + + operator std::string() const { return os.str(); } +}; + +template +inline fmt_sender_proxy fmt(const TYPE& val, const Args&... manips) +{ + return fmt_sender_proxy(val, manips...); +} + +template +inline TR& operator<<(TR& ot, const fmt_sender_proxy& formatter) +{ + formatter.sendto(ot); + return ot; +} +} + #if HAVE_CXX11 template @@ -673,16 +728,7 @@ class UniquePtr: public std::auto_ptr template inline std::string Sprint(const Arg1& arg) { - return srt::sfmts(arg); -} - -// Ok, let it be 2-arg, in case when a manipulator is needed -template -inline std::string Sprint(const Arg1& arg1, const Arg2& arg2) -{ - srt::obufstream sout; - sout << arg1 << arg2; - return sout.str(); + return fmt_sender_proxy(arg).str(); } template @@ -715,7 +761,8 @@ typename Map::mapped_type const* map_getp(const Map& m, const Key& key) #endif -template inline +#if 0 +template inline std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) { srt::obufstream os; @@ -740,14 +787,21 @@ std::string Printable(const Container& in, std::pair/*pseudoargument os << "]"; return os.str(); } - +#endif template inline std::string Printable(const Container& in) { using namespace srt_pair_op; typedef typename Container::value_type Value; - return Printable(in, Value()); + + srt::obufstream os; + os << "[ "; + typedef typename Container::const_iterator it_t; + for (it_t i = in.begin(); i != in.end(); ++i) + os << Value(*i) << " "; + os << "]"; + return os.str(); } // Printable with prefix added for every element. From f4088e41ffb531bcd2c2d9ea5890dabd021be7f5 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 25 Jun 2024 13:32:12 +0200 Subject: [PATCH 150/517] Removed the use of sfmt obufstream --- apps/logsupport.cpp | 2 +- apps/uriparser.cpp | 2 +- srtcore/common.cpp | 4 ++-- srtcore/handshake.cpp | 2 +- srtcore/logging.cpp | 6 ++--- srtcore/logging.h | 16 ++++++------- srtcore/utilities.h | 55 ++++++++++++++++++++++++++++++++++--------- 7 files changed, 59 insertions(+), 28 deletions(-) diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index 86deb1690..16a9332df 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -173,7 +173,7 @@ set SrtParseLogFA(string fa, set* punknown) void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) { - srt::obufstream son, soff; + ostringstream son, soff; for (auto& s: speclist) { diff --git a/apps/uriparser.cpp b/apps/uriparser.cpp index 7425bca03..87687a621 100644 --- a/apps/uriparser.cpp +++ b/apps/uriparser.cpp @@ -64,7 +64,7 @@ string UriParser::makeUri() prefix = m_proto + "://"; } - srt::obufstream out; + ostringstream out; out << prefix << m_host; if ((m_port == "" || m_port == "0") && m_expect == EXPECT_FILE) diff --git a/srtcore/common.cpp b/srtcore/common.cpp index acef9ee77..1a41cfde1 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -277,12 +277,12 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc } else { - obufstream peeraddr_form; + std::stringstream peeraddr_form; peeraddr_form << fmt(peeraddr16[0], hex, setfill('0'), setw(4)); for (int i = 1; i < 8; ++i) peeraddr_form << ":" << fmt(peeraddr16[i], hex, setfill('0'), setw(4)); - LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form); + LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form.rdbuf()); *target_ipv4_addr = 0; if (peer.family() != AF_INET) { diff --git a/srtcore/handshake.cpp b/srtcore/handshake.cpp index f8f03c84d..c97b4e2a3 100644 --- a/srtcore/handshake.cpp +++ b/srtcore/handshake.cpp @@ -300,7 +300,7 @@ std::string srt::SrtFlagString(int32_t flags) #define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0]))) std::string output; - static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" }; + static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI", "FilterCapable" }; size_t i = 0; for (; i < LEN(namera); ++i) diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index 1c51821e2..bc3ad0eec 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -43,7 +43,7 @@ LogDispatcher::Proxy LogDispatcher::operator()() return Proxy(*this); } -void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) +void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) { using namespace std; using namespace srt; @@ -67,7 +67,7 @@ void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) // Note: ThreadName::get needs a buffer of size min. ThreadName::BUFSIZE if (!isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf)) { - serr << "/" << tmp_buf; + serr << rawstr("/") << tmp_buf; } if (!isset(SRT_LOGF_DISABLE_SEVERITY)) @@ -76,7 +76,7 @@ void LogDispatcher::CreateLogLinePrefix(srt::obufstream& serr) serr.write(prefix, prefix_len); // include terminal 0 } - serr << ": "; + serr << rawstr(": "); } std::string LogDispatcher::Proxy::ExtractName(std::string pretty_function) diff --git a/srtcore/logging.h b/srtcore/logging.h index 3e8dfd07f..30466badd 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -28,8 +28,6 @@ written by #include #endif -//#include "srt_sfmt.h" - #include "srt.h" #include "utilities.h" #include "threadname.h" @@ -194,7 +192,7 @@ struct SRT_API LogDispatcher bool CheckEnabled(); - void CreateLogLinePrefix(srt::obufstream&); + void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); // log.Debug("This is the ", nth, " time"); <--- C++11 only. @@ -287,7 +285,7 @@ struct LogDispatcher::Proxy { LogDispatcher& that; - srt::obufstream os; + std::ostringstream os; // Cache the 'enabled' state in the beginning. If the logging // becomes enabled or disabled in the middle of the log, we don't @@ -380,7 +378,7 @@ struct LogDispatcher::Proxy if (that_enabled) { if ((flags & SRT_LOGF_DISABLE_EOL) == 0) - os << srt::seol; + os << std::endl; that.SendLogLine(i_file, i_line, area, os.str()); } // Needed in destructor? @@ -420,7 +418,7 @@ struct LogDispatcher::Proxy buf[len-1] = '\0'; } - os << buf; + os.write(buf, len); return *this; } }; @@ -474,10 +472,10 @@ inline bool LogDispatcher::CheckEnabled() //extern std::mutex Debug_mutex; -inline void PrintArgs(srt::obufstream&) {} +inline void PrintArgs(std::ostringstream&) {} template -inline void PrintArgs(srt::obufstream& serr, Arg1&& arg1, Args&&... args) +inline void PrintArgs(std::ostringstream& serr, Arg1&& arg1, Args&&... args) { serr << std::forward(arg1); PrintArgs(serr, args...); @@ -485,7 +483,7 @@ inline void PrintArgs(srt::obufstream& serr, Arg1&& arg1, Args&&... args) // Add exceptional handling for sync::atomic template -inline void PrintArgs(srt::obufstream& serr, const srt::sync::atomic& arg1, Args&&... args) +inline void PrintArgs(std::ostringstream& serr, const srt::sync::atomic& arg1, Args&&... args) { serr << arg1.load(); PrintArgs(serr, args...); diff --git a/srtcore/utilities.h b/srtcore/utilities.h index f85d5b23f..e5ef025fd 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -45,8 +45,6 @@ written by #include #include -#include "srt_sfmt.h" - // -------------- UTILITIES ------------------------ // --- ENDIAN --- @@ -485,7 +483,7 @@ class FixedArray void throw_invalid_index(int i) const { - srt::obufstream ss; + std::ostringstream ss; ss << "Index " << i << "out of range"; throw std::runtime_error(ss.str()); } @@ -620,6 +618,38 @@ inline TR& operator<<(TR& ot, const fmt_sender_proxy& formatter) formatter.sendto(ot); return ot; } + +template +struct check_minus_1 +{ + static const size_t value = N - 1; +}; + +template<> +struct check_minus_1<0> +{ +}; + +struct rawstr +{ + const char* d; + size_t s; + + rawstr(const char* dd, size_t ss): d(dd), s(ss) {} + const char* data() const { return d; } + size_t size() const { return s; } + + template + explicit rawstr(const char (&ref)[N]): d(ref), s(check_minus_1::value) {} +}; + +template +Stream& operator<<(Stream& out, rawstr v) +{ + out.write(v.data(), v.size()); + return out; +} + } #if HAVE_CXX11 @@ -642,7 +672,7 @@ inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) template inline std::string Sprint(Args&&... args) { - srt::obufstream sout; + std::ostringstream sout; Print(sout, args...); return sout.str(); } @@ -765,7 +795,7 @@ typename Map::mapped_type const* map_getp(const Map& m, const Key& key) template inline std::string Printable(const Container& in, Value /*pseudoargument*/, const char* fmt = 0) { - srt::obufstream os; + std::ostringstream os; os << "[ "; typedef typename Container::const_iterator it_t; for (it_t i = in.begin(); i != in.end(); ++i) @@ -779,7 +809,7 @@ template inline std::string Printable(const Container& in, std::pair/*pseudoargument*/, const char* fmtk = 0, const char* fmtv = 0) { using namespace srt_pair_op; - srt::obufstream os; + std::ostringstream os; os << "[ "; typedef typename Container::const_iterator it_t; for (it_t i = in.begin(); i != in.end(); ++i) @@ -795,7 +825,7 @@ std::string Printable(const Container& in) using namespace srt_pair_op; typedef typename Container::value_type Value; - srt::obufstream os; + std::ostringstream os; os << "[ "; typedef typename Container::const_iterator it_t; for (it_t i = in.begin(); i != in.end(); ++i) @@ -811,7 +841,7 @@ std::string PrintableMod(const Container& in, const std::string& prefix) { using namespace srt_pair_op; typedef typename Container::value_type Value; - srt::obufstream os; + std::ostringstream os; os << "[ "; for (typename Container::const_iterator y = in.begin(); y != in.end(); ++y) os << prefix << Value(*y) << " "; @@ -1034,11 +1064,14 @@ inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) if ( size == 0 ) return ""; - srt::obufstream os; + using namespace std; + + ostringstream os; + os << setfill('0') << setw(2) << hex << uppercase; for (size_t i = 0; i < size; ++i) { - os << srt::sfmt(bytes[i], "02X"); + os << int(bytes[i]); } return os.str(); } @@ -1204,7 +1237,7 @@ inline std::string BufferStamp(const char* mem, size_t size) } // Convert to hex string - return srt::sfmts(sum, "08X"); + return srt::fmt(sum, setfill('0'), setw(8), hex, uppercase); } template From e48a6cbd05219fd7d63fd8866a18be2986631718 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Tue, 25 Jun 2024 15:37:28 +0200 Subject: [PATCH 151/517] Removed named reference to avoid dangling ref warning --- srtcore/logging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srtcore/logging.h b/srtcore/logging.h index 30466badd..a3c8aaa4e 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -53,7 +53,7 @@ written by { \ srt_logging::LogDispatcher::Proxy log(logdes); \ log.setloc(__FILE__, __LINE__, __FUNCTION__); \ - const srt_logging::LogDispatcher::Proxy& log_prox SRT_ATR_UNUSED = args; \ + { (void)(const srt_logging::LogDispatcher::Proxy&)(args); } \ } // LOGF uses printf-like style formatting. From 2ba12a0b67e993447a937421b99e585149c18776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Tue, 25 Jun 2024 16:01:03 +0200 Subject: [PATCH 152/517] Added lacking include for sstream --- srtcore/utilities.h | 1 + 1 file changed, 1 insertion(+) diff --git a/srtcore/utilities.h b/srtcore/utilities.h index e5ef025fd..4a8e26f18 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -35,6 +35,7 @@ written by #include #include #include +#include #if HAVE_CXX11 #include From f16fed782235e414f30692bca37d9280808ef840 Mon Sep 17 00:00:00 2001 From: Mikolaj Malecki Date: Wed, 26 Jun 2024 12:20:30 +0200 Subject: [PATCH 153/517] Cleaned up previous usage of sfmt facilities --- srtcore/{ => ATTIC}/srt_sfmt.h | 0 srtcore/api.cpp | 4 ++-- srtcore/core.cpp | 4 ++-- srtcore/sync.cpp | 12 ++++++------ srtcore/sync.h | 5 ++--- srtcore/utilities.h | 1 + 6 files changed, 13 insertions(+), 13 deletions(-) rename srtcore/{ => ATTIC}/srt_sfmt.h (100%) diff --git a/srtcore/srt_sfmt.h b/srtcore/ATTIC/srt_sfmt.h similarity index 100% rename from srtcore/srt_sfmt.h rename to srtcore/ATTIC/srt_sfmt.h diff --git a/srtcore/api.cpp b/srtcore/api.cpp index 840ef3acd..7e5a01356 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -231,7 +231,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) if (sock == 0) return ""; - srt::obufstream os; + std::stringstream os; os << "@" << sock << ":"; return os.str(); } @@ -3249,7 +3249,7 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer& m = i->second; #if ENABLE_HEAVY_LOGGING - srt::obufstream that_muxer; + std::stringstream that_muxer; that_muxer << "id=" << m.m_iID << " port=" << m.m_iPort << " ip=" << (m.m_iIPversion == AF_INET ? "v4" : "v6"); #endif diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 9eec4a9ae..ec00c34d2 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -10012,9 +10012,9 @@ int srt::CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - obufstream buf; + std::stringstream buf; // Take the last 2 ciphers from the socket ID. - string s = sfmts(m_SocketID, "02"); + string s = fmt(m_SocketID, setfill('0'), setw(2)).str(); buf << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); const string thname = buf.str(); #else diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index af193c603..e2061d2ed 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -51,16 +51,16 @@ std::string FormatTime(const steady_clock::time_point& timestamp) const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; steady_clock::time_point frac = timestamp - seconds_from(total_sec); - srt::obufstream out; + std::stringstream out; if (days) - out << days << "D "; + out << days << rawstr("D "); out << srt::fmt(hours, setfill('0'), setw(2)) << ":" << srt::fmt(minutes, setfill('0'), setw(2)) << ":" << srt::fmt(seconds, setfill('0'), setw(2)) << "." << srt::fmt(frac.time_since_epoch().count(), setfill('0'), setw(decimals)) - << " [STDY]"; + << rawstr(" [STDY]"); return out.str(); } @@ -76,10 +76,10 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) char tmp_buf[512]; strftime(tmp_buf, 512, "%X.", &tm); - srt::obufstream out; - out << tmp_buf + std::stringstream out; + out << rawstr(tmp_buf) << srt::fmt(count_microseconds(timestamp.time_since_epoch()) % 1000000, setfill('0'), setw(6)) - << " [SYST]"; + << rawstr(" [SYST]"); return out.str(); } diff --git a/srtcore/sync.h b/srtcore/sync.h index 40584a838..dbdbe7d20 100644 --- a/srtcore/sync.h +++ b/srtcore/sync.h @@ -55,7 +55,6 @@ #include "srt.h" #include "utilities.h" #include "srt_attr_defs.h" -#include "srt_sfmt.h" namespace srt { @@ -775,8 +774,8 @@ struct DurationUnitName template inline std::string FormatDuration(const steady_clock::duration& dur) { - obufstream out; - out << sfmt(DurationUnitName::count(dur), "f") << DurationUnitName::name(); + std::stringstream out; + out << fmt(DurationUnitName::count(dur), std::fixed) << DurationUnitName::name(); return out.str(); } diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 4a8e26f18..c7d8d89ae 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -605,6 +605,7 @@ class fmt_sender_proxy } operator std::string() const { return os.str(); } + std::string str() const { return os.str(); } }; template From a49bdd963b7c03fcf01e89b2f733a5c83804e95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Ma=C5=82ecki?= Date: Thu, 27 Jun 2024 15:12:33 +0200 Subject: [PATCH 154/517] Provided C++03 version of fmt --- srtcore/common.cpp | 4 +- srtcore/congctl.cpp | 2 +- srtcore/core.cpp | 16 +++--- srtcore/logging.cpp | 6 +-- srtcore/logging.h | 3 ++ srtcore/queue.cpp | 2 +- srtcore/socketconfig.cpp | 8 +-- srtcore/sync.cpp | 26 +++++----- srtcore/sync.h | 2 +- srtcore/utilities.h | 103 +++++++++++++++++++++++++++++++++------ 10 files changed, 125 insertions(+), 47 deletions(-) diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 1a41cfde1..707babe42 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -278,9 +278,9 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc else { std::stringstream peeraddr_form; - peeraddr_form << fmt(peeraddr16[0], hex, setfill('0'), setw(4)); + peeraddr_form << (fmt(peeraddr16[0]) << hex << setfill('0') << setw(4)); for (int i = 1; i < 8; ++i) - peeraddr_form << ":" << fmt(peeraddr16[i], hex, setfill('0'), setw(4)); + peeraddr_form << ":" << (fmt(peeraddr16[i]) << hex << setfill('0'), setw(4)); LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form.rdbuf()); *target_ipv4_addr = 0; diff --git a/srtcore/congctl.cpp b/srtcore/congctl.cpp index 012d766b5..f5e6400b0 100644 --- a/srtcore/congctl.cpp +++ b/srtcore/congctl.cpp @@ -595,7 +595,7 @@ class FileCC : public SrtCongestionControlBase { m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval); HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize=" - << fmt(m_dCWndSize, setprecision(6)) << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); + << (fmt(m_dCWndSize) << setprecision(6)) << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")"); } } else diff --git a/srtcore/core.cpp b/srtcore/core.cpp index ec00c34d2..a353b26c6 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -2107,8 +2107,8 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint } LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << " vers=0x" << fmt(srtdata[SRT_HS_VERSION], hex) - << " opts=0x" << fmt(srtdata[SRT_HS_FLAGS], hex) + << " vers=0x" << (fmt(srtdata[SRT_HS_VERSION]) << hex) + << " opts=0x" << (fmt(srtdata[SRT_HS_FLAGS]) << hex) << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; @@ -2343,7 +2343,7 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion) - << " Flags: SND:" << fmt(m_uPeerSrtFlags, hex, setfill('0'), setw(8)) + << " Flags: SND:" << (fmt(m_uPeerSrtFlags) << hex << setfill('0') << setw(8)) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); // Basic version check if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) @@ -5470,14 +5470,14 @@ void * srt::CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " - << (timediff_us / 1000) << "." << fmt(timediff_us % 1000, fixed, setfill('0'), setw(3)) << " ms"); + << (timediff_us / 1000) << "." << (fmt(timediff_us % 1000) << fixed << setfill('0') << setw(3)) << " ms"); #endif string why; if (self->frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) { LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno << " delayed for " << (timediff_us / 1000) << "." - << fmt(timediff_us % 1000, fixed, setfill('0'), setw(3)) << " ms " << why); + << (fmt(timediff_us % 1000) << fixed << setfill('0') << setw(3)) << " ms " << why); } #if SRT_ENABLE_FREQUENT_LOG_TRACE else @@ -7777,7 +7777,7 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" - << fmt(cgwindow, setprecision(3))); + << (fmt(cgwindow) << setprecision(3))); #endif } @@ -8418,7 +8418,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // included, but it also triggers for any other kind of invalid value. // This check MUST BE DONE before making any operation on this number. LOGC(inlog.Error, log << CONID() << "ACK: IPE/EPE: received invalid ACK value: " << ackdata_seqno - << " " << fmt(ackdata_seqno, hex) << " (IGNORED)"); + << " " << (fmt(ackdata_seqno) << hex) << " (IGNORED)"); return; } @@ -10014,7 +10014,7 @@ int srt::CUDT::checkLazySpawnTsbPdThread() #if ENABLE_HEAVY_LOGGING std::stringstream buf; // Take the last 2 ciphers from the socket ID. - string s = fmt(m_SocketID, setfill('0'), setw(2)).str(); + string s = (fmt(m_SocketID) << setfill('0') << setw(2)).str(); buf << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); const string thname = buf.str(); #else diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index bc3ad0eec..9a18666eb 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -60,14 +60,14 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) { - serr << tmp_buf << fmt(tv.tv_usec, setfill('0'), setw(6)); + serr << tmp_buf << (fmt(tv.tv_usec) << setfill('0') << setw(6)); } } // Note: ThreadName::get needs a buffer of size min. ThreadName::BUFSIZE if (!isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf)) { - serr << rawstr("/") << tmp_buf; + serr << SRTRSTR("/") << tmp_buf; } if (!isset(SRT_LOGF_DISABLE_SEVERITY)) @@ -76,7 +76,7 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) serr.write(prefix, prefix_len); // include terminal 0 } - serr << rawstr(": "); + serr << SRTRSTR(": "); } std::string LogDispatcher::Proxy::ExtractName(std::string pretty_function) diff --git a/srtcore/logging.h b/srtcore/logging.h index a3c8aaa4e..1763a8328 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -336,6 +336,9 @@ struct LogDispatcher::Proxy return *this; } + // Provide explicit overloads for const char* and string + // so that printing them bypasses the formatting facility + // Special case for atomics, as passing them to snprintf() call // requires unpacking the real underlying value. template diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index a5dfde68d..20e2ab363 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -1079,7 +1079,7 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, { HLOGC(cnlog.Debug, log << "RID: socket @" << i->m_iID << " still active (remaining " - << fmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, fixed) << "s of TTL)..."); + << (fmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << fixed) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 955a9006d..7e2faa865 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -744,8 +744,8 @@ struct CSrtConfigSetter { co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << fmt(km_refresh, std::hex) << ": setting SRTO_KMPREANNOUNCE=0x" - << fmt(co.uKmPreAnnouncePkt, std::hex)); + log << "SRTO_KMREFRESHRATE=0x" << (fmt(km_refresh) << std::hex) << ": setting SRTO_KMPREANNOUNCE=0x" + << (fmt(co.uKmPreAnnouncePkt) << std::hex)); } } }; @@ -770,8 +770,8 @@ struct CSrtConfigSetter if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << fmt(km_preanno, std::hex) - << " exceeds KmRefresh/2, 0x" << fmt((kmref - 1) / 2, std::hex) + log << "SRTO_KMPREANNOUNCE=0x" << (fmt(km_preanno) << std::hex) + << " exceeds KmRefresh/2, 0x" << (fmt((kmref - 1) / 2) << std::hex) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index e2061d2ed..34de6586c 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -53,14 +53,13 @@ std::string FormatTime(const steady_clock::time_point& timestamp) steady_clock::time_point frac = timestamp - seconds_from(total_sec); std::stringstream out; if (days) - out << days << rawstr("D "); - - out << srt::fmt(hours, setfill('0'), setw(2)) << ":" - << srt::fmt(minutes, setfill('0'), setw(2)) << ":" - << srt::fmt(seconds, setfill('0'), setw(2)) << "." - << srt::fmt(frac.time_since_epoch().count(), - setfill('0'), setw(decimals)) - << rawstr(" [STDY]"); + out << days << SRTRSTR("D "); + + out << (srt::fmt(hours) << setfill('0') << setw(2)) << SRTRSTR(":") + << (srt::fmt(minutes) << setfill('0'), setw(2)) << SRTRSTR(":") + << (srt::fmt(seconds) << setfill('0'), setw(2)) << SRTRSTR(".") + << (srt::fmt(frac.time_since_epoch().count()) << setfill('0') << setw(decimals)) + << SRTRSTR(" [STDY]"); return out.str(); } @@ -74,12 +73,15 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) const time_t tt = now_s + delta_s; struct tm tm = SysLocalTime(tt); // in seconds char tmp_buf[512]; - strftime(tmp_buf, 512, "%X.", &tm); + size_t tmp_size = strftime(tmp_buf, 512, "%X.", &tm); + // Mind the theoretically possible erro case + if (!tmp_size) + return "