diff --git a/.github/workflows/abi.yml b/.github/workflows/abi.yml new file mode 100644 index 000000000..2c05cc06b --- /dev/null +++ b/.github/workflows/abi.yml @@ -0,0 +1,61 @@ +name: ABI checks + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + SRT_BASE: v1.5.0 + +jobs: + build: + name: ABI checks + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + with: + path: pull_request + - name: configure + run: | + cd pull_request + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build + run: | + sudo apt install -y abi-dumper + cd pull_request/_build && cmake --build ./ + make install DESTDIR=./installdir + SRT_TAG_VERSION=$(cat version.h |grep SRT_VERSION_MINOR |head -n1 |awk {'print $3'}) + abi-dumper libsrt.so -o libsrt-pr.dump -public-headers installdir/usr/local/include/srt/ -lver 0 + SRT_BASE="v1.$SRT_TAG_VERSION.0" + echo "SRT_BASE=$SRT_BASE" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + path: tag + ref: ${{ env.SRT_BASE }} + - name: configure_tag + run: | + echo $SRT_TAG_VERSION + cd tag + mkdir _build && cd _build + cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_UNITTESTS=ON ../ + - name: build_tag + run: | + cd tag + cd _build && cmake --build ./ + make install DESTDIR=./installdir + abi-dumper libsrt.so -o libsrt-tag.dump -public-headers installdir/usr/local/include/srt/ -lver 1 + - name: abi-check + run: | + git clone https://github.com/lvc/abi-compliance-checker.git + cd abi-compliance-checker && sudo make install && cd ../ + abi-compliance-checker -l libsrt -old tag/_build/libsrt-tag.dump -new pull_request/_build/libsrt-pr.dump + RES=$? + if (( $RES != 0 )) + then + echo "ABI/API Compatibility check failed with value $?" + exit $RES + fi diff --git a/.github/workflows/cxx11-ubuntu.yaml b/.github/workflows/cxx11-ubuntu.yaml index 500ff1beb..aa74e1fce 100644 --- a/.github/workflows/cxx11-ubuntu.yaml +++ b/.github/workflows/cxx11-ubuntu.yaml @@ -5,19 +5,34 @@ on: branches: [ master ] pull_request: branches: [ master ] - + types: [opened, synchronize, reopened] jobs: build: name: ubuntu runs-on: ubuntu-20.04 - + env: + BUILD_WRAPPER_OUT_DIR: sonar-output # Directory where build-wrapper output will be placed steps: - uses: actions/checkout@v3 + - name: Install sonar-scanner and build-wrapper + uses: sonarsource/sonarcloud-github-c-cpp@v2 - name: configure run: | mkdir _build && cd _build - cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON + cmake ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON -DENABLE_CODE_COVERAGE=ON - name: build - run: cd _build && cmake --build ./ + run: cd _build && build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build . - name: test - run: cd _build && ctest --extra-verbose + run: | + cd _build && ctest --extra-verbose + - name: codecov + run: | + source ./scripts/collect-gcov.sh + bash <(curl -s https://codecov.io/bash) + - name: Run SonarCloud Scan for C and C++ + if: ${{ !github.event.pull_request.head.repo.fork }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # Consult https://docs.sonarcloud.io/advanced-setup/ci-based-analysis/sonarscanner-cli/ for more information and options. + run: sonar-scanner --define sonar.cfamily.build-wrapper-output=_build/"${{ env.BUILD_WRAPPER_OUT_DIR }}" diff --git a/.github/workflows/s390x-focal.yaml b/.github/workflows/s390x-focal.yaml new file mode 100644 index 000000000..f1b6c7508 --- /dev/null +++ b/.github/workflows/s390x-focal.yaml @@ -0,0 +1,41 @@ +name: QEMU to run s390x-focal + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + Tests: + runs-on: ubuntu-latest + steps: + - name: Setup multiarch/qemu-user-static + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + - name: ubuntu-core:s390x-focal + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "uname -a && + lscpu | grep Endian + " + - name: Checkout + uses: actions/checkout@v3 + - name: configure + uses: docker://multiarch/ubuntu-core:s390x-focal + with: + args: > + bash -c + "apt-get -y update && + export DEBIAN_FRONTEND=noninteractive && + export TZ=Etc/UTC && + apt-get -y install tzdata && + uname -a && + lscpu | grep Endian && + apt-get -y install cmake g++ libssl-dev git && + mkdir _build && cd _build && + cmake ../ -DENABLE_ENCRYPTION=ON -DENABLE_UNITTESTS=ON -DENABLE_BONDING=ON -DENABLE_TESTING=ON -DENABLE_EXAMPLES=ON && + cmake --build ./ && + ./test-srt -disable-ipv6" diff --git a/.travis.yml b/.travis.yml index 72df01bf1..94d5dac3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,6 @@ addons: - build-essential - libmbedtls-dev - gdb - sonarcloud: - organization: "haivision" - token: - secure: "wJZC0kyyjuf4SZyonZ6p/5Ga9asEqSnKWF9NpRbu6S6ceERO7vbebuSJF5qX3A6ivPuw0TTk5WASOdnvIyfA28FU/D0MWRdH8K7T3w77wdE9EgAEYTUXzdrbzJY18+9pxjljHwWXWALPSGf3MClg4irWrdk1e6uHK+68R39+ZvBGBFpWeeZy/+at9+xwhtAGKBlSHe8zc+3wPxuYdvviLVJ25qbpNmnzkUR0X89G+UBl90raCPSN32EHFdImHZ5DxfEQQJgZFRjzQUY4EW/iYwaMel7jufAq0ClgV4psKujl9Lz8cPqx3WgqRfJyiIthOMTsac7G4zAw8LK2CI0VsssBp0JalLXaumi6vG7o6c3rIwKckzSKccq3pHa7h45praIVVn9s3nq+Q/JGA11FMkKQxdQtmwgFsLhbi6ZxabgsUi5KtWoWY2z6MgpJuROuAjNxZi9XJzUoJs7zSTUtRRW7V8Q2lRiOnknYh25N6TCA5bpyy1EZmRdJErm071YNI9P01gbFz5137FWJFiJzro9TGF0KoHSGiCIdUt3WlMzwr/i/wFLxFBQOZQ2rjTXvhs4hxONxMZV3gzxA1NdLaf9i5Mh6jxVMV+ujaRSV7JmPGzxqiAlpT9cJUhTCYuar9diLLeDrpe7RawEZR8V1xVDQ7yT8ruDNQ78VbSn/sC0=" homebrew: update: false packages: @@ -25,9 +21,7 @@ matrix: - os: linux env: - BUILD_TYPE=Debug - - BUILD_OPTS='-DENABLE_CODE_COVERAGE=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - - RUN_SONARCUBE=1 - - RUN_CODECOV=1 + - BUILD_OPTS='-DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' - env: - BUILD_TYPE=Debug - BUILD_OPTS='-DENABLE_LOGGING=OFF -DUSE_ENCLIB=mbedtls -DENABLE_MONOTONIC_CLOCK=ON -DENABLE_BONDING=ON -DCMAKE_CXX_FLAGS="-Werror"' @@ -81,17 +75,7 @@ script: export PKG_CONFIG_PATH=$(brew --prefix openssl)"/lib/pkgconfig"; cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS -DENABLE_UNITTESTS="ON"; fi - - echo "TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG" - - echo "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" - - if [[ "$TRAVIS_REPO_SLUG" != "Haivision/srt" || "$TRAVIS_PULL_REQUEST" -gt 0 ]]; then - export RUN_SONARCUBE=0; - fi - - echo "RUN_SONARCUBE=$RUN_SONARCUBE" - - if (( "$RUN_SONARCUBE" )); then - build-wrapper-linux-x86-64 --out-dir bw-output make; - else - make -j$(nproc); - fi + - make -j$(nproc); - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ulimit -c unlimited; ./test-srt -disable-ipv6; @@ -99,11 +83,3 @@ script: if [ -f core ]; then gdb -batch ./test-srt -c core -ex bt -ex "info thread" -ex quit; else echo "NO CORE - NO CRY!"; fi; test $SUCCESS == 0; fi -after_success: - - if (( "$RUN_CODECOV" )); then - source ./scripts/collect-gcov.sh; - bash <(curl -s https://codecov.io/bash); - fi - - if (( "$RUN_SONARCUBE" )); then - sonar-scanner -D"sonar.cfamily.gcov.reportPath=."; - fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b0e3cff..47d9b88df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSNAME_LC) set_if(DARWIN (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "iOS") OR (${CMAKE_SYSTEM_NAME} MATCHES "tvOS") - OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS")) + OR (${CMAKE_SYSTEM_NAME} MATCHES "watchOS") + OR (${CMAKE_SYSTEM_NAME} MATCHES "visionOS")) set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux") set_if(BSD ${SYSNAME_LC} MATCHES "bsd$") set_if(MICROSOFT WIN32 AND (NOT MINGW AND NOT CYGWIN)) @@ -73,7 +74,12 @@ if (NOT MICROSOFT) # that ENABLE_DEBUG is set as it should. if (ENABLE_DEBUG EQUAL 2) set (CMAKE_BUILD_TYPE "RelWithDebInfo") - add_definitions(-DNDEBUG) + if (ENABLE_ASSERT) + # Add _DEBUG macro if explicitly requested, to enable SRT_ASSERT(). + add_definitions(-D_DEBUG) + else() + add_definitions(-DNDEBUG) + endif() elseif (ENABLE_DEBUG) # 1, ON, YES, TRUE, Y, or any other non-zero number set (CMAKE_BUILD_TYPE "Debug") @@ -232,12 +238,12 @@ if (NOT USE_ENCLIB) message("NOTE: USE_GNUTLS is deprecated. Use -DUSE_ENCLIB=gnutls instead.") set (USE_ENCLIB gnutls) else() - set (USE_ENCLIB openssl) + set (USE_ENCLIB openssl-evp) endif() endif() set(USE_ENCLIB "${USE_ENCLIB}" CACHE STRING "The crypto library that SRT uses") -set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "gnutls" "mbedtls" "botan") +set_property(CACHE USE_ENCLIB PROPERTY STRINGS "openssl" "openssl-evp" "gnutls" "mbedtls" "botan") # Make sure DLLs and executabes go to the same path regardles of subdirectory set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) @@ -360,11 +366,11 @@ if (ENABLE_ENCRYPTION) set (SSL_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) set (SSL_LIBRARIES ${MBEDTLS_LIBRARIES}) endif() + if (WIN32) + set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) + endif() if ("${SSL_LIBRARIES}" STREQUAL "") set (SSL_LIBRARIES mbedtls mbedcrypto) - if (WIN32) - set (SSL_LIBRARIES ${SSL_LIBRARIES} bcrypt) - endif() endif() message(STATUS "SSL enforced mbedtls: -I ${SSL_INCLUDE_DIRS} -l;${SSL_LIBRARIES}") @@ -1522,15 +1528,11 @@ if (ENABLE_UNITTESTS AND ENABLE_CXX11) ) #set_tests_properties(test-srt PROPERTIES RUN_SERIAL TRUE) else() - gtest_add_tests( - TEST_LIST tests_srt - TARGET test-srt - ) set_tests_properties(${tests_srt} PROPERTIES RUN_SERIAL TRUE) + gtest_discover_tests(test-srt) endif() enable_testing() - endif() diff --git a/apps/srt-live-transmit.cpp b/apps/srt-live-transmit.cpp index a4ed93a29..0a918e6b0 100644 --- a/apps/srt-live-transmit.cpp +++ b/apps/srt-live-transmit.cpp @@ -65,7 +65,7 @@ #include #include - +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index 569a0c26e..90bd43eae 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -27,6 +27,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" // CreateAddr #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 10591888b..879d54086 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -80,7 +80,7 @@ inline void Print(Log& ) {} template inline void Print(Log& out, Arg1&& arg1, Args&&... args) { - out << arg1; + out << std::forward(arg1); Print(out, args...); } diff --git a/codecov.yml b/codecov.yml index f91e5c1fe..2716c7f60 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,3 +6,7 @@ coverage: threshold: null patch: false changes: false +ignore: + - "testing" + - "apps" + - "example" \ No newline at end of file diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index ef6f87513..a06f8556d 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -58,7 +58,7 @@ Exchange for the initial key is done in the handshake. - `SRT_KM_S_SECURED` (`2`): KM exchange was successful and the data will be sent encrypted and will be decrypted by the receiver. This state is only possible on -both sides in both directions simultaneously. +both sides in both directions simultaneously. Any unencrypted packet will be dropped by the receiver. - `SRT_KM_S_NOSECRET` (`3`): If this state is in the sending direction (`SRTO_SNDKMSTATE`), then it means that the sending party has set a passphrase, but the peer did not. @@ -301,7 +301,8 @@ connection is rejected - **however** you may also change the value of this option for the accepted socket in the listener callback (see `srt_listen_callback`) if an appropriate instruction was given in the Stream ID. -Currently supported congestion controllers are designated as "live" and "file" +Currently supported congestion controllers are designated as "live" and "file", +which correspond to the Live and File modes. Note that it is not recommended to change this option manually, but you should rather change the whole set of options using the [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. @@ -772,7 +773,8 @@ for more details. | `SRTO_LATENCY` | 1.0.2 | pre | `int32_t` | ms | 120 * | 0.. | RW | GSD | This option sets both [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY) and [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) -to the same value specified. +to the same value specified. Note that the default value for `SRTO_RCVLATENCY` is modified by the +[`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE) option. Prior to SRT version 1.3.0 `SRTO_LATENCY` was the only option to set the latency. However it is effectively equivalent to setting `SRTO_PEERLATENCY` in the sending direction @@ -1212,6 +1214,8 @@ considered broken on timeout. The latency value (as described in [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY)) provided by the sender side as a minimum value for the receiver. +This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabled. + Reading the value of the option on an unconnected socket reports the configured value. Reading the value on a connected socket reports the effective receiver buffering latency of the peer. @@ -1296,16 +1300,22 @@ This value is only significant when [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) is enabl **Default value**: 120 ms in Live mode, 0 in File mode (see [`SRTO_TRANSTYPE`](#SRTO_TRANSTYPE)). The latency value defines the **minimum** receiver buffering delay before delivering an SRT data packet -from a receiving SRT socket to a receiving application. The provided value is used in the connection establishment (handshake exchange) stage -to fix the end-to-end latency of the transmission. The effective end-to-end latency `L` will be fixed -as the network transmission time of the final handshake packet (~1/2 RTT) plus the **negotiated** latency value `Ln`. -Data packets will stay in the receiver buffer for at least `L` microseconds since the timestamp of the -packet, independent of the actual network transmission times (RTT variations) of these packets. +from a receiving SRT socket to a receiving application. The actual value of the receiver buffering delay `Ln` (the negotiated latency) used on a connection is determined by the negotiation in the connection establishment (handshake exchange) phase as the maximum of the `SRTO_RCVLATENCY` value and the value of [`SRTO_PEERLATENCY`](#SRTO_PEERLATENCY) set by the peer. +The general idea for the latency mechanism is to keep the time distance between two consecutive +received packets the same as the time when these same packets were scheduled for sending by the +sender application (or per the time explicitly declared when sending - see +[`srt_sendmsg2`](API-functions.md#srt_sendmsg2) for details). This keeps any packets that have arrived +earlier than their delivery time in the receiver buffer until their delivery time comes. This should +compensate for any jitter in the network and provides an extra delay needed for a packet retransmission. + +For detailed information on how the latency setting influences the actual packet delivery time and +how this time is defined, refer to the [latency documentation](../features/latency.md). + Reading the `SRTO_RCVLATENCY` value on a socket after the connection is established provides the actual (negotiated) latency value `Ln`. @@ -1638,9 +1648,19 @@ enabled in sender if receiver supports it. Sets the transmission type for the socket, in particular, setting this option sets multiple other parameters to their default values as required for a -particular transmission type. +particular transmission type. This sets the following options to their defaults +in particular mode: + +* [`SRTO_CONGESTION`](#SRTO_CONGESTION) +* [`SRTO_MESSAGEAPI`](#SRTO_MESSAGEAPI) +* [`SRTO_NAKREPORT`](#SRTO_NAKREPORT) +* [`SRTO_RCVLATENCY`](#SRTO_RCVLATENCY), also set as [`SRTO_LATENCY`](#SRTO_LATENCY) +* [`SRTO_TLPKTDROP`](#SRTO_TLPKTDROP) +* [`SRTO_TSBPDMODE`](#SRTO_TSBPDMODE) + + -Values defined by enum `SRT_TRANSTYPE` (see above for possible values) +Values defined by enum [`SRT_TRANSTYPE`](#SRT_TRANSTYPE). [Return to list](#list-of-options) diff --git a/docs/API/statistics.md b/docs/API/statistics.md index 60bba59ee..bc34ecca8 100644 --- a/docs/API/statistics.md +++ b/docs/API/statistics.md @@ -245,6 +245,8 @@ Packets may be dropped conditionally when both `SRTO_TSBPDMODE` and `SRTO_TLPKTD #### pktRcvUndecryptTotal The total number of packets that failed to be decrypted at the receiver side. Available for receiver. +The statistic also counts unencrypted packets that were expected to be uncrypted on a secured connection (see [SRTO_KM_S_SECURED](API-socket-options.md#srt_km_state)) +and hence dropped as not encrypted (undecrypted). #### pktSndFilterExtraTotal @@ -822,4 +824,4 @@ The ratio of unrecovered by the socket group packets `Dropped Packets Ratio` can ``` Dropped Packets Ratio = pktRcvDropTotal / pktSentUniqueTotal; in case both sender and receiver statistics is available Dropped Packets Ratio = pktRcvDropTotal / (pktRecvUniqueTotal + pktRcvDropTotal); in case receiver only statistics is available -``` \ No newline at end of file +``` diff --git a/docs/build/build-options.md b/docs/build/build-options.md index 88fcb85bb..529bd5cd7 100644 --- a/docs/build/build-options.md +++ b/docs/build/build-options.md @@ -597,8 +597,8 @@ remember that: Encryption library to be used. Possible options for ``: -* openssl (default) -* openssl-evp (OpenSSL EVP API, since 1.5.1) +* openssl-evp (default) +* openssl * gnutls (with nettle) * mbedtls * botan diff --git a/docs/features/latency.md b/docs/features/latency.md new file mode 100644 index 000000000..d287ac030 --- /dev/null +++ b/docs/features/latency.md @@ -0,0 +1,219 @@ +## General statement about latency + +In the live streaming there are many things happening between the +camera's lens and the screen of the video player, all of which contribute +to a delay that is generally referred to as "latency". This overall latency +includes the time it takes for the camera frame grabber device to pass +frames to the encoder, encoding, multiplexing, **sending over the network**, +splitting, decoding and then finally displaying. + +In SRT, however, "latency" is defined as only the delay introduced by **sending +over the network**. It's the time between the moment when the `srt_sendmsg2` +function is called at the sender side up to the moment when the `srt_recvmsg2` +function is called at the receiver side. This SRT latency is the actual time difference +between these two events. + + +## The goal of the latency (TSBPD) mechanism + +SRT employs a TimeStamp Based Packet Delivery (TSBPD) mechanism +with strict goal of keeping the time interval between two consecutive packets +on the receiver side identical to what they were at the sender side. This +requires introducing an extra delay that should define when exactly the packet +can be retrieved by the receiver application -- if the packet arrives early, it must +wait in the receiver buffer until the delivery time. This time for a packet N +is roughly defined as: + +``` +PTS[N] = ETS[N] + LATENCY(option) +``` + +where `ETS[N]` is the time when the packet would arrive, if all delays +from the network and the processing software on both sides are identical +to what they were for the very first received data packet. This means that +for the very first packet `ETS[0]` is equal to this packet's arrival time. +For every following packet the delivery time interval should be equal to the +that packet's declared scheduling time interval. + + +## SRT's approach to packet arrival time + +SRT provides two socket options `SRTO_PEERLATENCY` and `SRTO_RCVLATENCY`. +While they have "latency" in their names, they do *not* define the true time +interval between the `srt_sendmsg2` and `srt_recvmsg2` calls for the same +packet. They are only used to add an extra delay (at the receiver side) to +the time when the packet "should" arrive (ETS). This extra delay is used to +compensate for two things: + +* an extra network delay (that is, if the packet arrived later than it +"should have arrived"), or + +* a packet retransmission. + +Note that many of the values included in these formulas are not controllable and +some cannot be measured directly. In many cases there are measured values +that are sums of other values, but the component values can't be extracted. + +There are two values that we can obtain at the receiver side: + +* ATS: actual arrival time, which is the time when the UDP packet +has been extracted through the `recvmsg` system call. + +* TS: time recorded in the packet header, set on the sender side and extracted +from the packet at the receiver side + +Note that the timestamp in the packet's header is 32-bit, which gives +it more or less 2.5 minutes to roll over. Therefore timestamp +rollover is tracked and a segment increase is performed in order to keep an +eye on the overall actual time. For the needs of the formula definitions +it must be stated that TS is the true difference between the connection +start time and the time when the sending time has been declared when +the sender application is calling any of the `srt_send*` functions +(see [`srt_sendmsg2`](../API/API-functions.md#srt_sendmsg2) for details). + + +## SRT latency components + +To understand the latency components we need also other definitions: + +* **ETS** (Expected Time Stamp): The packet's expected arrival time, when it +"should" arrive according to its timestamp + +* **PTS** (Presentation Time Stamp): The packet's play time, when SRT gives the packet +to the `srt_recvmsg2` call (that is, it sets up the IN flag in epoll +and resumes the blocked function call, if it was in blocking mode). + +* **STS** (Sender Time Stamp): The time when the packet was +scheduled for sending at the sender side (if you don't use the +declared time, by default it's the monotonic time used when this +function is called). + +* **RTS** (Receiver Time Stamp): The same as STS, but calculated at the receiver side. The +only way to extract it is by using some initial statements. + +The "true latency" for a particular packet in SRT can be simply defined as: + +* `TD = PTS - STS` + +Note that this is a stable definition (independent of the packet), +but this value is not really controllable. So let's define the PTS +for a packet `x`: + +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +where `LATENCY` is the negotiated latency value (out of the +`SRTO_RCVLATENCY` on the agent and `SRTO_PEERLATENCY` on the peer) +and DRIFT will be described later (for simplification you can +state it's initially 0). + +These components undergo the following formula: + +* `ETS[x] = start_time + TS[x]` + +Note that it's not possible to simply define a "true" latency based on STS +because the sender and receiver are two different machines that can only +see one another through the network. Their clocks are separate, +and can even run at different or changing speeds, and the only +visible phenomena happen when packets arrive at the receiver machine. +However, the formula above does allow us to define the start time because +we state the following for the very first data packet: + +* `ETS[0] = ATS[0]` + +This means that from this formula we can define the start time: + +* `start_time = ATS[0] - TS[0]` + +Therefore we can state that if we have two identical clocks on +both machines with identical time bases and speeds, then: + +* `ATS[x] = program_delay[x] + network_delay[x] + STS[x]` + +Note that two machines communicating over a network do not typically have a +common clock base. Therefore, although this formula is correct, it involves +components that can neither be measured nor captured at the receiver side. + +This formula for ATS doesn't apply to the real latency, which is based strictly +on ETS. But you can apply this formula for the very first arriving packet, +because in this case they are equal: `ATS[0] = ETS[0]`. + +Therefore this formula is true for the very first packet: + +* `ETS[0] = prg_delay[0] + net_delay[0] + STS[0]` + +We know also that the TS set on the sender side is: + +* `TS[x] = STS[x] - snd_connect_time` + +Taking both formulas for ETS together: + +* `ETS[x] = start_time + TS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x]` + +we have then: + +* `start_time = prg_delay[0] + net_delay[0] + snd_connect_time` + +**IMPORTANT**: `start_time` is not the time of arrival of the first packet, +but that time taken backwards by using the delay already recorded in TS. As TS should +represent the delay towards `snd_connect_time`, `start_time` should be simply the same +as `snd_connect_time`, just on the receiver side, and so shifted by the +first packet's delays of `prg_delay` and `net_delay`. + +So, as we have the start time defined, the above formulas: + +* `ETS[x] = start_time + TS[x]` +* `PTS[x] = ETS[x] + LATENCY + DRIFT` + +now define the packet delivery time as: + +* `PTS[x] = start_time + TS[x] + LATENCY + DRIFT` + +and after replacing the start time we have: + +* `PTS[x] = prg_delay[0] + net_delay[0] + snd_connect_time + TS[x] + LATENCY + DRIFT` + +and from the TS formula we get STS, so we replace it: + +* `PTS[x] = prg_delay[0] + net_delay[0] + STS[x] + LATENCY + DRIFT` + +We can now get the true network latency in SRT by moving STS to the other side: + +* `PTS[x] - STS[x] = prg_delay[0] + net_delay[0] + LATENCY + DRIFT` + + +## The DRIFT + +The DRIFT is a measure of the variance over time of the base time. +To simplify the calculations above, DRIFT is considered to be 0, +which is the initial state. In time, however, it changes based on the +value of the Arrival Time Deviation: + +* `ATD[x] = ATS[x] - ETS[x]` + +The drift is then calculated as: + +* `DRIFT[x] = average(ATD[x-N] ... ATD[x])` + +The value of the drift is tracked over an appropriate number of samples. If +it exceeds a threshold value, the drift value is applied to modify the +base time. However, as you can see from the formula for ATD, the drift is +simply taken from the actual time when the packet arrived, and the time +when it would have arrived if the `prg_delay` and `net_delay` values were +exactly the same as for the very first packet. ATD then represents the +changes in these values. There can be two main factors that could result +in having this value as non-zero: + +1. A phenomenon has been observed in several types of networks where +the very first packet arrives quickly, but as subsequent data packets +come in regularly, the network delay slightly increases and then remains fixed +for a long time at this increased value. This phenomenon can be +mitigated by having a reliable value for RTT. Once the increase is observed +a special factor could be applied to decrease the positive value +of the drift. This isn't currently implemented. This phenomenon also +isn't observed in every network, especially those covering longer distances. + +2. The clock speed on both machines (sender and receiver) isn't exactly the same, +which means that if you decipher the ETS basing on the TS, over time it may result +in values that even precede the STS (suggesting a negative network delay) or that +have an enormous delay (with ATS exceeding PTS). This is actually the main reason +for tracking the drift. diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c index 2568654b1..dc3f06801 100644 --- a/haicrypt/hcrypt.c +++ b/haicrypt/hcrypt.c @@ -320,6 +320,7 @@ int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handl cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT; memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt)); cryptoClone->ctx_pair[0].salt_len = 0; + cryptoClone->ctx = &cryptoClone->ctx_pair[0]; } *phhc = (void *)cryptoClone; diff --git a/scripts/visionOS.cmake b/scripts/visionOS.cmake new file mode 100644 index 000000000..0f0f60b69 --- /dev/null +++ b/scripts/visionOS.cmake @@ -0,0 +1,171 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for VISIONOS development + +# Options: +# +# VISION_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 +# This decides if SDKS will be selected from the XROS.platform or XRSimulator.platform folders +# OS - the default, used to build for Vision Pro physical device, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# VISIONOS_ARCH = arm64 (default for OS), x86_64 (addiitonal support for SIMULATOR64) +# +# CMAKE_VISIONOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the VISIONOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_VISIONOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_VISIONOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_VISIONOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version +# + +# Standard settings +set (CMAKE_SYSTEM_NAME Darwin) +set (CMAKE_SYSTEM_VERSION 1) +set (UNIX True) +set (APPLE True) +set (VISIONOS True) + +# Required as of cmake 2.8.10 +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for visionOs" FORCE) + +# Determine the cmake host system version so we know where to find the visionOS SDKs +find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) +if (CMAKE_UNAME) + execute_process(COMMAND uname -r + OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) + string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") +endif (CMAKE_UNAME) + + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG) + set(VISIONOS_DEBUG_OPTIONS "-glldb -gmodules") +else() + set(VISIONOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden") +endif() + +set (CMAKE_C_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") +set (CMAKE_CXX_FLAGS_INIT "${VISIONOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}") + +set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}") + + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +if (NOT DEFINED PKG_CONFIG_EXECUTABLE) + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) + if (DEFINED PKG_CONFIG_EXECUTABLE) + execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING) + endif(DEFINED PKG_CONFIG_EXECUTABLE) +endif(NOT DEFINED PKG_CONFIG_EXECUTABLE) + + +# fffio Specify path to install shared library on device +set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks") +set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE) + +# Setup visionOS platform unless specified manually with VISIONOS_PLATFORM +if (NOT DEFINED VISIONOS_PLATFORM) + set (VISIONOS_PLATFORM "OS") +endif (NOT DEFINED VISIONOS_PLATFORM) +set (VISIONOS_PLATFORM ${VISIONOS_PLATFORM} CACHE STRING "Type of visionOS Platform") + +# Check the platform selection and setup for developer root +if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_PLATFORM_LOCATION "XROS.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xros") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (SIMULATOR true) + set (VISIONOS_PLATFORM_LOCATION "XRSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator") +else (${VISIONOS_PLATFORM} STREQUAL OS) + message (FATAL_ERROR "Unsupported VISIONOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${VISIONOS_PLATFORM} STREQUAL OS) + +# Setup visionOS developer location unless specified manually with CMAKE_VISIONOS_DEVELOPER_ROOT +if (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) + execute_process(COMMAND /usr/bin/xcode-select -print-path + OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) + string(STRIP "${CMAKE_XCODE_DEVELOPER_DIR}" CMAKE_XCODE_DEVELOPER_DIR) # FIXED: remove new line character, otherwise it complain no visionOS SDK's found in default search path + set (CMAKE_VISIONOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${VISIONOS_PLATFORM_LOCATION}/Developer") +endif (NOT DEFINED CMAKE_VISIONOS_DEVELOPER_ROOT) +set (CMAKE_VISIONOS_DEVELOPER_ROOT ${CMAKE_VISIONOS_DEVELOPER_ROOT} CACHE PATH "Location of visionOS Platform") + +# Find and use the most recent visionOS sdk unless specified manually with CMAKE_VISIONOS_SDK_ROOT +if (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) + file (GLOB _CMAKE_VISIONOS_SDKS "${CMAKE_VISIONOS_DEVELOPER_ROOT}/SDKs/*") + if (_CMAKE_VISIONOS_SDKS) + list (SORT _CMAKE_VISIONOS_SDKS) + list (REVERSE _CMAKE_VISIONOS_SDKS) + list (GET _CMAKE_VISIONOS_SDKS 0 CMAKE_VISIONOS_SDK_ROOT) + else (_CMAKE_VISIONOS_SDKS) + message (FATAL_ERROR "No visionOS SDK's found in default search path ${CMAKE_VISIONOS_DEVELOPER_ROOT}. Manually set CMAKE_VISIONOS_SDK_ROOT or install the visionOS SDK.") + endif (_CMAKE_VISIONOS_SDKS) + message (STATUS "Toolchain using default visionOS SDK: ${CMAKE_VISIONOS_SDK_ROOT}") +endif (NOT DEFINED CMAKE_VISIONOS_SDK_ROOT) +set (CMAKE_VISIONOS_SDK_ROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Location of the selected visionOS SDK") + +# Set the sysroot default to the most recent SDK +set (CMAKE_OSX_SYSROOT ${CMAKE_VISIONOS_SDK_ROOT} CACHE PATH "Sysroot used for visionOS support") + +# set the architecture for visionOS +if (NOT DEFINED VISIONOS_ARCH) + if (${VISIONOS_PLATFORM} STREQUAL OS) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR) + set (VISIONOS_ARCH arm64) + elseif (${VISIONOS_PLATFORM} STREQUAL SIMULATOR64) + set (VISIONOS_ARCH x86_64) + endif (${VISIONOS_PLATFORM} STREQUAL OS) +endif(NOT DEFINED VISIONOS_ARCH) +set (CMAKE_OSX_ARCHITECTURES ${VISIONOS_ARCH} CACHE STRING "Build architecture for visionOS") + +# Set the find root to the visionOS developer roots and to user defined paths +set (CMAKE_FIND_ROOT_PATH ${CMAKE_VISIONOS_DEVELOPER_ROOT} ${CMAKE_VISIONOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "visionOS find search path root") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_VISIONOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_VISIONOS_SDK_ROOT}/Developer/Library/Frameworks +) + +# only search the visionOS sdks, not the remainder of the host filesystem (except for programs, so that we can still find Python if needed) +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/sonar-project.properties b/sonar-project.properties index 22b16d549..d11323df8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -19,7 +19,6 @@ sonar.organization=haivision # ===================================================== sonar.links.homepage=https://github.com/Haivision/srt -sonar.links.ci=https://travis-ci.org/Haivision/srt sonar.links.scm=https://github.com/Haivision/srt sonar.links.issue=https://github.com/Haivision/srt/issues @@ -29,8 +28,9 @@ sonar.links.issue=https://github.com/Haivision/srt/issues # ===================================================== # SQ standard properties -sonar.sources=. +sonar.sources=srtcore/,apps/,common/,examples/,haicrypt/,scripts/,testing/ +sonar.tests=test/ # Properties specific to the C/C++ analyzer: -sonar.cfamily.build-wrapper-output=bw-output +sonar.cfamily.build-wrapper-output=_build/sonar-output sonar.cfamily.gcov.reportsPath=. diff --git a/srtcore/api.cpp b/srtcore/api.cpp index fb28f328c..80709c67b 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -148,8 +148,7 @@ void srt::CUDTSocket::setBrokenClosed() bool srt::CUDTSocket::readReady() { - // TODO: Use m_RcvBufferLock here (CUDT::isRcvReadReady())? - if (m_UDT.m_bConnected && m_UDT.m_pRcvBuffer->isRcvDataReady()) + if (m_UDT.m_bConnected && m_UDT.isRcvBufferReady()) return true; if (m_UDT.m_bListening) @@ -657,6 +656,9 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, HLOGC(cnlog.Debug, log << "newConnection: mapping peer " << ns->m_PeerID << " to that socket (" << ns->m_SocketID << ")"); m_PeerRec[ns->getPeerSpec()].insert(ns->m_SocketID); + + LOGC(cnlog.Note, log << "@" << ns->m_SocketID << " connection on listener @" << listen + << " (" << ns->m_SelfAddr.str() << ") from peer @" << ns->m_PeerID << " (" << peer.str() << ")"); } catch (...) { @@ -764,7 +766,7 @@ int srt::CUDTUnited::newConnection(const SRTSOCKET listen, enterCS(ls->m_AcceptLock); try { - ls->m_QueuedSockets.insert(ns->m_SocketID); + ls->m_QueuedSockets[ns->m_SocketID] = ns->m_PeerAddr; } catch (...) { @@ -1108,8 +1110,22 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int } else if (ls->m_QueuedSockets.size() > 0) { - set::iterator b = ls->m_QueuedSockets.begin(); - u = *b; + map::iterator b = ls->m_QueuedSockets.begin(); + + if (pw_addr != NULL && pw_addrlen != NULL) + { + // Check if the length of the buffer to fill the name in + // was large enough. + const int len = b->second.size(); + if (*pw_addrlen < len) + { + // In case when the address cannot be rewritten, + // DO NOT accept, but leave the socket in the queue. + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + } + + u = b->first; ls->m_QueuedSockets.erase(b); accepted = true; } @@ -1180,14 +1196,8 @@ SRTSOCKET srt::CUDTUnited::accept(const SRTSOCKET listen, sockaddr* pw_addr, int if (pw_addr != NULL && pw_addrlen != NULL) { - // Check if the length of the buffer to fill the name in - // was large enough. - const int len = s->m_PeerAddr.size(); - if (*pw_addrlen < len) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - - memcpy((pw_addr), &s->m_PeerAddr, len); - *pw_addrlen = len; + memcpy((pw_addr), s->m_PeerAddr.get(), s->m_PeerAddr.size()); + *pw_addrlen = s->m_PeerAddr.size(); } return u; @@ -1395,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 = "setting group-derived option: #" + Sprint(g.m_config[i].so); + error_reason = "group-derived option: #" + Sprint(g.m_config[i].so); ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], (int)g.m_config[i].value.size()); } @@ -2647,20 +2657,23 @@ void srt::CUDTUnited::checkBrokenSockets() if (elapsed < milliseconds_from(CUDT::COMM_CLOSE_BROKEN_LISTENER_TIMEOUT_MS)) continue; } - else if ((s->core().m_pRcvBuffer != NULL) - // FIXED: calling isRcvDataAvailable() just to get the information - // whether there are any data waiting in the buffer, - // NOT WHETHER THEY ARE ALSO READY TO PLAY at the time when - // this function is called (isRcvDataReady also checks if the - // available data is "ready to play"). - && s->core().m_pRcvBuffer->hasAvailablePackets()) + else { - const int bc = s->core().m_iBrokenCounter.load(); - if (bc > 0) + CUDT& u = s->core(); + + enterCS(u.m_RcvBufferLock); + bool has_avail_packets = u.m_pRcvBuffer && u.m_pRcvBuffer->hasAvailablePackets(); + leaveCS(u.m_RcvBufferLock); + + if (has_avail_packets) { - // if there is still data in the receiver buffer, wait longer - s->core().m_iBrokenCounter.store(bc - 1); - continue; + const int bc = u.m_iBrokenCounter.load(); + if (bc > 0) + { + // if there is still data in the receiver buffer, wait longer + s->core().m_iBrokenCounter.store(bc - 1); + continue; + } } } @@ -2696,31 +2709,34 @@ void srt::CUDTUnited::checkBrokenSockets() for (sockets_t::iterator j = m_ClosedSockets.begin(); j != m_ClosedSockets.end(); ++j) { + CUDTSocket* ps = j->second; + CUDT& u = ps->core(); + // HLOGC(smlog.Debug, log << "checking CLOSED socket: " << j->first); - if (!is_zero(j->second->core().m_tsLingerExpiration)) + if (!is_zero(u.m_tsLingerExpiration)) { // asynchronous close: - if ((!j->second->core().m_pSndBuffer) || (0 == j->second->core().m_pSndBuffer->getCurrBufSize()) || - (j->second->core().m_tsLingerExpiration <= steady_clock::now())) + if ((!u.m_pSndBuffer) || (0 == u.m_pSndBuffer->getCurrBufSize()) || + (u.m_tsLingerExpiration <= steady_clock::now())) { - HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << j->second->m_SocketID); - j->second->core().m_tsLingerExpiration = steady_clock::time_point(); - j->second->core().m_bClosing = true; - j->second->m_tsClosureTimeStamp = steady_clock::now(); + HLOGC(smlog.Debug, log << "checkBrokenSockets: marking CLOSED qualified @" << ps->m_SocketID); + u.m_tsLingerExpiration = steady_clock::time_point(); + u.m_bClosing = true; + ps->m_tsClosureTimeStamp = steady_clock::now(); } } // timeout 1 second to destroy a socket AND it has been removed from // RcvUList const steady_clock::time_point now = steady_clock::now(); - const steady_clock::duration closed_ago = now - j->second->m_tsClosureTimeStamp; + const steady_clock::duration closed_ago = now - ps->m_tsClosureTimeStamp; if (closed_ago > seconds_from(1)) { - CRNode* rnode = j->second->core().m_pRNode; + CRNode* rnode = u.m_pRNode; if (!rnode || !rnode->m_bOnList) { HLOGC(smlog.Debug, - log << "checkBrokenSockets: @" << j->second->m_SocketID << " closed " + log << "checkBrokenSockets: @" << ps->m_SocketID << " closed " << FormatDuration(closed_ago) << " ago and removed from RcvQ - will remove"); // HLOGC(smlog.Debug, log << "will unref socket: " << j->first); @@ -2779,23 +2795,24 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // if it is a listener, close all un-accepted sockets in its queue // and remove them later - for (set::iterator q = s->m_QueuedSockets.begin(); q != s->m_QueuedSockets.end(); ++q) + for (map::iterator q = s->m_QueuedSockets.begin(); + q != s->m_QueuedSockets.end(); ++ q) { - sockets_t::iterator si = m_Sockets.find(*q); + sockets_t::iterator si = m_Sockets.find(q->first); if (si == m_Sockets.end()) { // gone in the meantime LOGC(smlog.Error, - log << "removeSocket: IPE? socket @" << (*q) << " being queued for listener socket @" - << s->m_SocketID << " is GONE in the meantime ???"); + log << "removeSocket: IPE? socket @" << (q->first) << " being queued for listener socket @" + << s->m_SocketID << " is GONE in the meantime ???"); continue; } CUDTSocket* as = si->second; as->breakSocket_LOCKED(); - m_ClosedSockets[*q] = as; - m_Sockets.erase(*q); + m_ClosedSockets[q->first] = as; + m_Sockets.erase(q->first); } } @@ -2818,8 +2835,20 @@ void srt::CUDTUnited::removeSocket(const SRTSOCKET u) // delete this one m_ClosedSockets.erase(i); + // XXX This below section can unlock m_GlobControlLock + // just for calling CUDT::closeInternal(), which is needed + // to avoid locking m_ConnectionLock after m_GlobControlLock, + // while m_ConnectionLock orders BEFORE m_GlobControlLock. + // This should be perfectly safe thing to do after the socket + // ID has been erased from m_ClosedSockets. No container access + // is done in this case. + // + // Report: P04-1.28, P04-2.27, P04-2.50, P04-2.55 + HLOGC(smlog.Debug, log << "GC/removeSocket: closing associated UDT @" << u); + leaveCS(m_GlobControlLock); s->core().closeInternal(); + enterCS(m_GlobControlLock); HLOGC(smlog.Debug, log << "GC/removeSocket: DELETING SOCKET @" << u); delete s; HLOGC(smlog.Debug, log << "GC/removeSocket: socket @" << u << " DELETED. Checking muxer."); @@ -2945,7 +2974,7 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons bool reuse_attempt = false; for (map::iterator i = m_mMultiplexer.begin(); i != m_mMultiplexer.end(); ++i) { - CMultiplexer& m = i->second; + CMultiplexer const& m = i->second; // First, we need to find a multiplexer with the same port. if (m.m_iPort != port) @@ -3829,7 +3858,7 @@ int srt::CUDT::getsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, void* pw_optval int srt::CUDT::setsockopt(SRTSOCKET u, int, SRT_SOCKOPT optname, const void* optval, int optlen) { - if (!optval) + if (!optval || optlen < 0) return APIError(MJ_NOTSUP, MN_INVAL, 0); try @@ -4675,21 +4704,18 @@ void setloglevel(LogLevel::type ll) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.max_level = ll; - srt_logger_config.updateLoggersState(); } void addlogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, true); - srt_logger_config.updateLoggersState(); } void dellogfa(LogFA fa) { ScopedLock gg(srt_logger_config.mutex); srt_logger_config.enabled_fa.set(fa, false); - srt_logger_config.updateLoggersState(); } void resetlogfa(set fas) @@ -4697,7 +4723,6 @@ void resetlogfa(set fas) ScopedLock gg(srt_logger_config.mutex); for (int i = 0; i <= SRT_LOGFA_LASTNONE; ++i) srt_logger_config.enabled_fa.set(i, fas.count(i)); - srt_logger_config.updateLoggersState(); } void resetlogfa(const int* fara, size_t fara_size) @@ -4706,7 +4731,6 @@ void resetlogfa(const int* fara, size_t fara_size) srt_logger_config.enabled_fa.reset(); for (const int* i = fara; i != fara + fara_size; ++i) srt_logger_config.enabled_fa.set(*i, true); - srt_logger_config.updateLoggersState(); } void setlogstream(std::ostream& stream) diff --git a/srtcore/api.h b/srtcore/api.h index 9ba77d23a..fddbfc294 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -151,7 +151,7 @@ class CUDTSocket CUDT m_UDT; //< internal SRT socket logic public: - std::set m_QueuedSockets; //< set of connections waiting for accept() + std::map m_QueuedSockets; //< set of connections waiting for accept() sync::Condition m_AcceptCond; //< used to block "accept" call sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index 7ea0945bc..2ec42487d 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -206,7 +206,7 @@ int CRcvBuffer::insert(CUnit* unit) return 0; } -int CRcvBuffer::dropUpTo(int32_t seqno) +std::pair CRcvBuffer::dropUpTo(int32_t seqno) { IF_RCVBUF_DEBUG(ScopedLog scoped_log); IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropUpTo: seqno " << seqno << " m_iStartSeqNo " << m_iStartSeqNo); @@ -215,16 +215,23 @@ int CRcvBuffer::dropUpTo(int32_t seqno) if (len <= 0) { IF_RCVBUF_DEBUG(scoped_log.ss << ". Nothing to drop."); - return 0; + return std::make_pair(0, 0); } m_iMaxPosOff -= len; if (m_iMaxPosOff < 0) m_iMaxPosOff = 0; - const int iDropCnt = len; + int iNumDropped = 0; // Number of dropped packets that were missing. + int iNumDiscarded = 0; // The number of dropped packets that existed in the buffer. while (len > 0) { + // Note! Dropping a EntryState_Read must not be counted as a drop because it was read. + // Note! Dropping a EntryState_Drop must not be counted as a drop because it was already dropped and counted earlier. + if (m_entries[m_iStartPos].status == EntryState_Avail) + ++iNumDiscarded; + else if (m_entries[m_iStartPos].status == EntryState_Empty) + ++iNumDropped; dropUnitInPos(m_iStartPos); m_entries[m_iStartPos].status = EntryState_Empty; SRT_ASSERT(m_entries[m_iStartPos].pUnit == NULL && m_entries[m_iStartPos].status == EntryState_Empty); @@ -239,14 +246,14 @@ int CRcvBuffer::dropUpTo(int32_t seqno) // If the nonread position is now behind the starting position, set it to the starting position and update. // Preceding packets were likely missing, and the non read position can probably be moved further now. - if (CSeqNo::seqcmp(m_iFirstNonreadPos, m_iStartPos) < 0) + if (!isInRange(m_iStartPos, m_iMaxPosOff, m_szSize, m_iFirstNonreadPos)) { m_iFirstNonreadPos = m_iStartPos; updateNonreadPos(); } if (!m_tsbpd.isEnabled() && m_bMessageAPI) updateFirstReadableOutOfOrder(); - return iDropCnt; + return std::make_pair(iNumDropped, iNumDiscarded); } int CRcvBuffer::dropAll() @@ -255,7 +262,8 @@ int CRcvBuffer::dropAll() return 0; const int end_seqno = CSeqNo::incseq(m_iStartSeqNo, m_iMaxPosOff); - return dropUpTo(end_seqno); + const std::pair numDropped = dropUpTo(end_seqno); + return numDropped.first + numDropped.second; } int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, DropActionIfExists actionOnExisting) diff --git a/srtcore/buffer_rcv.h b/srtcore/buffer_rcv.h index d4b50fab7..f783ac2a2 100644 --- a/srtcore/buffer_rcv.h +++ b/srtcore/buffer_rcv.h @@ -66,8 +66,8 @@ class CRcvBuffer /// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno). /// @param [in] seqno drop units up to this sequence number - /// @return number of dropped packets. - int dropUpTo(int32_t seqno); + /// @return number of dropped (missing) and discarded (available) packets as a pair(dropped, discarded). + std::pair dropUpTo(int32_t seqno); /// @brief Drop all the packets in the receiver buffer. /// The starting position and seqno are shifted right after the last packet in the buffer. @@ -200,6 +200,20 @@ class CRcvBuffer return (m_iMaxPosOff == 0); } + /// Returns the currently used number of cells, including + /// gaps with empty cells, or in other words, the distance + /// between the initial position and the youngest received packet. + size_t size() const + { + return m_iMaxPosOff; + } + + // Returns true if the buffer is full. Requires locking. + bool full() const + { + return size() == capacity(); + } + /// Return buffer capacity. /// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer". /// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously. @@ -239,9 +253,15 @@ class CRcvBuffer inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; } inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); } inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); } + + /// @brief Compares the two positions in the receiver buffer relative to the starting position. + /// @param pos2 a position in the receiver buffer. + /// @param pos1 a position in the receiver buffer. + /// @return a positive value if pos2 is ahead of pos1; a negative value, if pos2 is behind pos1; otherwise returns 0. inline int cmpPos(int pos2, int pos1) const { - // XXX maybe not the best implementation, but this keeps up to the rule + // XXX maybe not the best implementation, but this keeps up to the rule. + // Maybe use m_iMaxPosOff to ensure a position is not behind the m_iStartPos. const int off1 = pos1 >= m_iStartPos ? pos1 - m_iStartPos : pos1 + (int)m_szSize - m_iStartPos; const int off2 = pos2 >= m_iStartPos ? pos2 - m_iStartPos : pos2 + (int)m_szSize - m_iStartPos; @@ -327,9 +347,8 @@ class CRcvBuffer EntryStatus status; }; - //static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; } - - FixedArray m_entries; + typedef FixedArray entries_t; + entries_t m_entries; const size_t m_szSize; // size of the array of units (buffer) CUnitQueue* m_pUnitQueue; // the shared unit queue diff --git a/srtcore/buffer_snd.cpp b/srtcore/buffer_snd.cpp index eb16e3341..ef1bc498c 100644 --- a/srtcore/buffer_snd.cpp +++ b/srtcore/buffer_snd.cpp @@ -219,7 +219,7 @@ void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl) } m_pLastBlock = s; - m_iCount += iNumBlocks; + m_iCount = m_iCount + iNumBlocks; m_iBytesCount += len; m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len); @@ -293,7 +293,7 @@ int CSndBuffer::addBufferFromFile(fstream& ifs, int len) m_pLastBlock = s; enterCS(m_BufLock); - m_iCount += iNumBlocks; + m_iCount = m_iCount + iNumBlocks; m_iBytesCount += total; leaveCS(m_BufLock); @@ -317,7 +317,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, w_packet.m_pcData = m_pCurrBlock->m_pcData; readlen = m_pCurrBlock->m_iLength; w_packet.setLength(readlen, m_iBlockLen); - w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo; + w_packet.set_seqno(m_pCurrBlock->m_iSeqNo); // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0). // 2. The readData() is called to get the original (unique) payload not ever sent yet. @@ -343,7 +343,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, } Block* p = m_pCurrBlock; - w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset; + w_packet.set_msgflags(m_pCurrBlock->m_iMsgNoBitset); w_srctime = m_pCurrBlock->m_tsOriginTime; m_pCurrBlock = m_pCurrBlock->m_pNext; @@ -358,7 +358,7 @@ int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: picked up packet to send: size=" << readlen << " #" << w_packet.getMsgSeq() - << " %" << w_packet.m_iSeqNo + << " %" << w_packet.seqno() << " !" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); break; @@ -422,9 +422,10 @@ int32_t CSndBuffer::getMsgNoAt(const int offset) return p->getMsgSeq(); } -int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen) +int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, DropRange& w_drop) { - int32_t& msgno_bitset = w_packet.m_iMsgNo; + // NOTE: w_packet.m_iSeqNo is expected to be set to the value + // of the sequence number with which this packet should be sent. ScopedLock bufferguard(m_BufLock); @@ -439,19 +440,23 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if (p == m_pLastBlock) { LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!"); - return 0; + return READ_NONE; } #if ENABLE_HEAVY_LOGGING const int32_t first_seq = p->m_iSeqNo; int32_t last_seq = p->m_iSeqNo; #endif + // This is rexmit request, so the packet should have the sequence number + // already set when it was once sent uniquely. + SRT_ASSERT(p->m_iSeqNo == w_packet.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, @@ -463,11 +468,11 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL)) { - int32_t msgno = p->getMsgSeq(); - w_msglen = 1; - p = p->m_pNext; - bool move = false; - while (p != m_pLastBlock && msgno == p->getMsgSeq()) + w_drop.msgno = p->getMsgSeq(); + int msglen = 1; + p = p->m_pNext; + bool move = false; + while (p != m_pLastBlock && w_drop.msgno == p->getMsgSeq()) { #if ENABLE_HEAVY_LOGGING last_seq = p->m_iSeqNo; @@ -477,18 +482,27 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p = p->m_pNext; if (move) m_pCurrBlock = p; - w_msglen++; + msglen++; } HLOGC(qslog.Debug, - log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", " - << w_msglen << " packets to drop, msgno=" << msgno); - - // If readData returns -1, then msgno_bitset is understood as a Message ID to drop. - // This means that in this case it should be written by the message sequence value only - // (not the whole 4-byte bitset written at PH_MSGNO). - msgno_bitset = msgno; - return -1; + log << "CSndBuffer::readData: due to TTL exceeded, %(" << first_seq << " - " << last_seq << "), " + << msglen << " packets to drop with #" << w_drop.msgno); + + // Theoretically as the seq numbers are being tracked, you should be able + // to simply take the sequence number from the block. But this is a new + // feature and should be only used after refax for the sender buffer to + // make it manage the sequence numbers inside, instead of by CUDT::m_iSndLastDataAck. + w_drop.seqno[DropRange::BEGIN] = w_packet.seqno(); + w_drop.seqno[DropRange::END] = CSeqNo::incseq(w_packet.seqno(), msglen - 1); + + // Note the rules: here `p` is pointing to the first block AFTER the + // message to be dropped, so the end sequence should be one behind + // the one for p. Note that the loop rolls until hitting the first + // packet that doesn't belong to the message or m_pLastBlock, which + // is past-the-end for the occupied range in the sender buffer. + SRT_ASSERT(w_drop.seqno[DropRange::END] == CSeqNo::decseq(p->m_iSeqNo)); + return READ_DROP; } w_packet.m_pcData = p->m_pcData; @@ -501,7 +515,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time // encrypted, and with all ENC flags already set. So, the first call to send // the packet originally (the other overload of this function) must set these // flags. - w_packet.m_iMsgNo = p->m_iMsgNoBitset; + w_packet.set_msgflags(p->m_iMsgNoBitset); w_srctime = p->m_tsOriginTime; // This function is called when packet retransmission is triggered. @@ -509,7 +523,7 @@ int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time p->m_tsRexmitTime = steady_clock::now(); HLOGC(qslog.Debug, - log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo + log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.seqno() << " size=" << readlen << " to send [REXMIT]"); return readlen; @@ -547,7 +561,7 @@ void CSndBuffer::ackData(int offset) if (move) m_pCurrBlock = m_pFirstBlock; - m_iCount -= offset; + m_iCount = m_iCount - offset; updAvgBufSize(steady_clock::now()); } @@ -653,7 +667,7 @@ int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_ { m_pCurrBlock = m_pFirstBlock; } - m_iCount -= dpkts; + m_iCount = m_iCount - dpkts; m_iBytesCount -= dbytes; w_bytes = dbytes; diff --git a/srtcore/buffer_snd.h b/srtcore/buffer_snd.h index 5da8c9631..afd52110b 100644 --- a/srtcore/buffer_snd.h +++ b/srtcore/buffer_snd.h @@ -116,6 +116,10 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) int addBufferFromFile(std::fstream& ifs, int len); + // Special values that can be returned by readData. + static const int READ_NONE = 0; + static const int READ_DROP = -1; + /// Find data position to pack a DATA packet from the furthest reading point. /// @param [out] packet the packet to read. /// @param [out] origintime origin time stamp of the message @@ -130,14 +134,29 @@ class CSndBuffer SRT_ATTR_EXCLUDES(m_BufLock) time_point peekNextOriginal() const; + struct DropRange + { + static const size_t BEGIN = 0, END = 1; + int32_t seqno[2]; + int32_t msgno; + }; /// Find data position to pack a DATA packet for a retransmission. + /// IMPORTANT: @a packet is [in,out] because it is expected to get set + /// the sequence number of the packet expected to be sent next. The sender + /// buffer normally doesn't handle sequence numbers and the consistency + /// between the sequence number of a packet already sent and kept in the + /// buffer is achieved by having the sequence number recorded in the + /// CUDT::m_iSndLastDataAck field that should represent the oldest packet + /// still in the buffer. /// @param [in] offset offset from the last ACK point (backward sequence number difference) - /// @param [out] packet the packet to read. - /// @param [out] origintime origin time stamp of the message - /// @param [out] msglen length of the message - /// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded). + /// @param [in,out] w_packet storage for the packet, preinitialized with sequence number + /// @param [out] w_origintime origin time stamp of the message + /// @param [out] w_drop the drop information in case when dropping is to be done instead + /// @retval >0 Length of the data read. + /// @retval READ_NONE No data available or @a offset points out of the buffer occupied space. + /// @retval READ_DROP The call requested data drop due to TTL exceeded, to be handled first. SRT_ATTR_EXCLUDES(m_BufLock) - int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen); + int readData(const int offset, CPacket& w_packet, time_point& w_origintime, DropRange& w_drop); /// Get the time of the last retransmission (if any) of the DATA packet. /// @param [in] offset offset from the last ACK point (backward sequence number difference) @@ -241,7 +260,11 @@ class CSndBuffer int m_iSize; // buffer size (number of packets) const int m_iBlockLen; // maximum length of a block holding packet payload and AUTH tag (excluding packet header). const int m_iAuthTagSize; // Authentication tag size (if GCM is enabled). - int m_iCount; // number of used blocks + + // NOTE: This is atomic AND under lock because the function getCurrBufSize() + // is returning it WITHOUT locking. Modification, however, must stay under + // a lock. + sync::atomic m_iCount; // number of used blocks int m_iBytesCount; // number of payload bytes in queue time_point m_tsLastOriginTime; diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 0b448d681..0a4e1e318 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -51,7 +51,6 @@ modified by *****************************************************************************/ #include "platform_sys.h" - #include #include // Logging #include @@ -143,14 +142,6 @@ srt::CChannel::CChannel() , m_bBindMasked(true) #endif { -#ifdef _WIN32 - SecureZeroMemory((PVOID)&m_SendOverlapped, sizeof(WSAOVERLAPPED)); - m_SendOverlapped.hEvent = WSACreateEvent(); - if (m_SendOverlapped.hEvent == NULL) { - LOGC(kmlog.Error, log << CONID() << "IPE: WSACreateEvent failed with error: " << NET_ERROR); - throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); - } -#endif #ifdef SRT_ENABLE_PKTINFO // Do the check for ancillary data buffer size, kinda assertion static const size_t CMSG_MAX_SPACE = sizeof (CMSGNodeIPv4) + sizeof (CMSGNodeIPv6); @@ -166,12 +157,7 @@ srt::CChannel::CChannel() #endif } -srt::CChannel::~CChannel() -{ -#ifdef _WIN32 - WSACloseEvent(m_SendOverlapped.hEvent); -#endif -} +srt::CChannel::~CChannel() {} void srt::CChannel::createSocket(int family) { @@ -189,6 +175,7 @@ void srt::CChannel::createSocket(int family) m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); cloexec_flag = true; #endif + #else // ENABLE_SOCK_CLOEXEC m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); #endif // ENABLE_SOCK_CLOEXEC @@ -197,17 +184,18 @@ void srt::CChannel::createSocket(int family) throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); #if ENABLE_SOCK_CLOEXEC -#ifdef _WIN32 - // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) -#else + if (cloexec_flag) { +#ifdef _WIN32 + // XXX ::SetHandleInformation(hInputWrite, HANDLE_FLAG_INHERIT, 0) +#else if (0 != set_cloexec(m_iSocket, 1)) { throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); } +#endif //_WIN32 } -#endif #endif // ENABLE_SOCK_CLOEXEC if ((m_mcfg.iIpV6Only != -1) && (family == AF_INET6)) // (not an error if it fails) @@ -683,8 +671,8 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif LOGC(kslog.Debug, - log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.m_iID - << " size=" << packet.getLength() << " pkt.ts=" << packet.m_iTimeStamp + log << "CChannel::sendto: SENDING NOW DST=" << addr.str() << " target=@" << packet.id() + << " size=" << packet.getLength() << " pkt.ts=" << packet.timestamp() << dsrc.str() << " " << packet.Info()); #endif @@ -747,7 +735,7 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka #endif // convert control information into network order - packet.toNL(); + packet.toNetworkByteOrder(); #ifndef _WIN32 msghdr mh; @@ -786,38 +774,65 @@ int srt::CChannel::sendto(const sockaddr_any& addr, CPacket& packet, const socka const int res = (int)::sendmsg(m_iSocket, &mh, 0); #else - DWORD size = (DWORD)(CPacket::HDR_SIZE + packet.getLength()); - int addrsize = addr.size(); + class WSAEventRef + { + public: + WSAEventRef() + : e(::WSACreateEvent()) + { + } + ~WSAEventRef() + { + ::WSACloseEvent(e); + e = NULL; + } + void reset() + { + ::WSAResetEvent(e); + } + WSAEVENT Handle() + { + return e; + } - int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &m_SendOverlapped, NULL); + private: + WSAEVENT e; + }; +#if !defined(__MINGW32__) && defined(ENABLE_CXX11) + thread_local WSAEventRef lEvent; +#else + WSAEventRef lEvent; +#endif + WSAOVERLAPPED overlapped; + ::SecureZeroMemory(&overlapped, sizeof(overlapped)); + overlapped.hEvent = lEvent.Handle(); + + DWORD size = (DWORD)(packet.m_PacketVector[0].size() + packet.m_PacketVector[1].size()); + int addrsize = addr.size(); + int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr.get(), addrsize, &overlapped, NULL); if (res == SOCKET_ERROR) { if (NET_ERROR == WSA_IO_PENDING) { - res = WSAWaitForMultipleEvents(1, &m_SendOverlapped.hEvent, TRUE, 100 /*ms*/, FALSE); - if (res == WAIT_FAILED) - { - LOGC(kslog.Warn, log << "CChannel::WSAWaitForMultipleEvents: failed with " << NET_ERROR); - res = -1; - } + DWORD dwFlags = 0; + const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &overlapped, &size, TRUE, &dwFlags); + if (bCompleted) + res = 0; else - { - DWORD dwFlags = 0; - const bool bCompleted = WSAGetOverlappedResult(m_iSocket, &m_SendOverlapped, &size, false, &dwFlags); - res = bCompleted ? 0 : -1; - } + LOGC(kslog.Warn, log << "CChannel::sendto call on ::WSAGetOverlappedResult failed with error: " << NET_ERROR); + lEvent.reset(); } else { LOGC(kmlog.Error, log << CONID() << "WSASendTo failed with error: " << NET_ERROR); } } - WSAResetEvent(m_SendOverlapped.hEvent); + res = (0 == res) ? size : -1; #endif - packet.toHL(); + packet.toHostByteOrder(); return res; } @@ -1066,25 +1081,7 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet } w_packet.setLength(recv_size - CPacket::HDR_SIZE); - - // convert back into local host order - // XXX use NtoHLA(). - // for (int i = 0; i < 4; ++ i) - // w_packet.m_nHeader[i] = ntohl(w_packet.m_nHeader[i]); - { - uint32_t* p = w_packet.m_nHeader; - for (size_t i = 0; i < SRT_PH_E_SIZE; ++i) - { - *p = ntohl(*p); - ++p; - } - } - - if (w_packet.isControl()) - { - for (size_t j = 0, n = w_packet.getLength() / sizeof(uint32_t); j < n; ++j) - *((uint32_t*)w_packet.m_pcData + j) = ntohl(*((uint32_t*)w_packet.m_pcData + j)); - } + w_packet.toHostByteOrder(); return RST_OK; diff --git a/srtcore/channel.h b/srtcore/channel.h index 6df7ec0ce..e12310001 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -49,13 +49,6 @@ written by modified by Haivision Systems Inc. *****************************************************************************/ -#if HAVE_CXX11 -#define SRT_ATR_ALIGNAS(n) alignas(n) -#elif HAVE_GCC -#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) -#else -#define SRT_ATR_ALIGNAS(n) -#endif #ifndef INC_SRT_CHANNEL_H #define INC_SRT_CHANNEL_H @@ -176,9 +169,6 @@ class CChannel private: UDPSOCKET m_iSocket; // socket descriptor -#ifdef _WIN32 - mutable WSAOVERLAPPED m_SendOverlapped; -#endif // Mutable because when querying original settings // this comprises the cache for extracted values, diff --git a/srtcore/common.cpp b/srtcore/common.cpp index fe5217dfd..6d747ecaa 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -68,8 +68,6 @@ modified by #include "packet.h" #include "threadname.h" -#include // SysStrError - using namespace std; using namespace srt::sync; using namespace srt_logging; diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 3f5fba410..5aaa83427 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -119,6 +119,7 @@ const int UDT::ERROR = srt::CUDT::ERROR; 2[15..0]: TsbPD delay [0..60000] msec */ +// IMPORTANT!!! This array must be ordered by value, because std::binary_search is performed on it! extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_SNDSYN, SRTO_RCVSYN, @@ -127,11 +128,14 @@ extern const SRT_SOCKOPT srt_post_opt_list [SRT_SOCKOPT_NPOST] = { SRTO_RCVTIMEO, SRTO_MAXBW, SRTO_INPUTBW, - SRTO_MININPUTBW, SRTO_OHEADBW, SRTO_SNDDROPDELAY, SRTO_DRIFTTRACER, + SRTO_MININPUTBW, SRTO_LOSSMAXTTL +#ifdef ENABLE_MAXREXMITBW + ,SRTO_MAXREXMITBW +#endif }; const int32_t @@ -294,7 +298,7 @@ void srt::CUDT::construct() m_iPeerTsbPdDelay_ms = 0; m_bPeerTsbPd = false; m_bTsbPd = false; - m_bTsbPdAckWakeup = false; + m_bTsbPdNeedsWakeup = false; m_bGroupTsbPd = false; m_bPeerTLPktDrop = false; m_bBufferWasFull = false; @@ -559,6 +563,15 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int32_t); break; +#ifdef ENABLE_MAXREXMITBW + case SRTO_MAXREXMITBW: + if (size_t(optlen) < sizeof(m_config.llMaxRexmitBW)) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + *(int64_t*)optval = m_config.llMaxRexmitBW; + optlen = sizeof(int64_t); + break; +#endif + case SRTO_STATE: *(int32_t *)optval = uglobal().getStatus(m_SocketID); optlen = sizeof(int32_t); @@ -1982,7 +1995,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; @@ -2088,9 +2101,9 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint return SRT_CMD_NONE; } - LOGC(cnlog.Note, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] - << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen + << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] + << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -2513,7 +2526,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSREQ(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_HSRSP. if (rescmd != SRT_CMD_HSRSP) { @@ -2540,7 +2553,7 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs, return false; // don't interpret } - int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.m_iTimeStamp, HS_VERSION_SRT1); + int rescmd = processSrtMsg_HSRSP(begin + 1, bytelen, hspkt.timestamp(), HS_VERSION_SRT1); // Interpreted? Then it should be responded with SRT_CMD_NONE. // (nothing to be responded for HSRSP, unless there was some kinda problem) if (rescmd != SRT_CMD_NONE) @@ -3572,7 +3585,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) // control of methods.) // ID = 0, connection request - reqpkt.m_iID = 0; + reqpkt.set_id(0); size_t hs_size = m_iMaxSRTPayloadSize; m_ConnReq.store_to((reqpkt.m_pcData), (hs_size)); @@ -3587,7 +3600,7 @@ void srt::CUDT::startConnect(const sockaddr_any& serv_addr, int32_t forced_isn) setPacketTS(reqpkt, tnow); HLOGC(cnlog.Debug, - log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.m_iTimeStamp + log << CONID() << "CUDT::startConnect: REQ-TIME set HIGH (TimeStamp: " << reqpkt.timestamp() << "). SENDING HS: " << m_ConnReq.show()); /* @@ -3659,7 +3672,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 { @@ -3903,17 +3916,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; @@ -3924,7 +3937,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, @@ -3944,7 +3957,7 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, if (m_RejectReason == SRT_REJ_UNKNOWN) m_RejectReason = SRT_REJ_ROGUE; - sendRendezvousRejection(serv_addr, (request)); + sendRendezvousRejection(serv_addr, (reqpkt)); status = false; } } @@ -3961,8 +3974,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, @@ -3974,7 +3987,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()); } } @@ -3984,16 +3997,16 @@ bool srt::CUDT::processAsyncConnectRequest(EReadStatus rst, /* XXX Shouldn't it send a single response packet for the rejection? // Set the version to 0 as "handshake rejection" status and serialize it CHandShake zhs; - size_t size = request.getLength(); - zhs.store_to((request.m_pcData), (size)); - request.setLength(size); + size_t size = reqpkt.getLength(); + zhs.store_to((reqpkt.m_pcData), (size)); + reqpkt.setLength(size); */ } HLOGC(cnlog.Debug, log << CONID() << "processAsyncConnectRequest: setting REQ-TIME HIGH, SENDING HS:" << m_ConnReq.show()); m_tsLastReqTime = steady_clock::now(); - m_pSndQueue->sendto(serv_addr, request, m_SourceAddr); + m_pSndQueue->sendto(serv_addr, reqpkt, m_SourceAddr); return status; } @@ -4961,8 +4974,9 @@ EConnectStatus srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, } */ - - LOGC(cnlog.Note, log << CONID() << "Connection established to: " << m_PeerAddr.str()); + + LOGC(cnlog.Note, log << CONID() << "Connection established from (" + << m_SourceAddr.str() << ") to peer @" << m_PeerID << " (" << m_PeerAddr.str() << ")"); return CONN_ACCEPT; } @@ -5397,7 +5411,7 @@ void * srt::CUDT::tsbpd(void* param) CUniqueSync recvdata_lcc (self->m_RecvLock, self->m_RecvDataCond); CSync tsbpd_cc(self->m_RcvTsbPdCond, recvdata_lcc.locker()); - self->m_bTsbPdAckWakeup = true; + self->m_bTsbPdNeedsWakeup = true; while (!self->m_bClosing) { steady_clock::time_point tsNextDelivery; // Next packet delivery time @@ -5417,6 +5431,21 @@ void * srt::CUDT::tsbpd(void* param) const bool is_time_to_deliver = !is_zero(info.tsbpd_time) && (tnow >= info.tsbpd_time); tsNextDelivery = info.tsbpd_time; +#if ENABLE_HEAVY_LOGGING + if (info.seqno == SRT_SEQNO_NONE) + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: NO PACKETS"); + } + else + { + HLOGC(tslog.Debug, log << self->CONID() << "sok/tsbpd: packet check: %" + << info.seqno << " T=" << FormatTime(tsNextDelivery) + << " diff-now-playtime=" << FormatDuration(tnow - tsNextDelivery) + << " ready=" << is_time_to_deliver + << " ondrop=" << info.seq_gap); + } +#endif + if (!self->m_bTLPktDrop) { rxready = !info.seq_gap && is_time_to_deliver; @@ -5462,8 +5491,8 @@ void * srt::CUDT::tsbpd(void* param) if (rxready) { HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " - << (count_milliseconds(steady_clock::now() - info.tsbpd_time)) << "ms)"); + log << self->CONID() << "tsbpd: PLAYING PACKET seq=" << info.seqno << " (belated " + << FormatDuration(steady_clock::now() - info.tsbpd_time) << ")"); /* * There are packets ready to be delivered * signal a waiting "recv" call if there is any data available @@ -5526,6 +5555,8 @@ void * srt::CUDT::tsbpd(void* param) if (self->m_bClosing) break; + SRT_ATR_UNUSED bool bWokeUpOnSignal = true; + if (!is_zero(tsNextDelivery)) { IF_HEAVY_LOGGING(const steady_clock::duration timediff = tsNextDelivery - tnow); @@ -5533,12 +5564,12 @@ void * srt::CUDT::tsbpd(void* param) * Buffer at head of queue is not ready to play. * Schedule wakeup when it will be. */ - self->m_bTsbPdAckWakeup = false; + self->m_bTsbPdNeedsWakeup = false; HLOGC(tslog.Debug, - log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno - << " T=" << FormatTime(tsNextDelivery) << " - waiting " << count_milliseconds(timediff) << "ms"); + log << self->CONID() << "tsbpd: FUTURE PACKET seq=" << info.seqno + << " T=" << FormatTime(tsNextDelivery) << " - waiting " << FormatDuration(timediff)); THREAD_PAUSED(); - tsbpd_cc.wait_until(tsNextDelivery); + bWokeUpOnSignal = tsbpd_cc.wait_until(tsNextDelivery); THREAD_RESUMED(); } else @@ -5555,20 +5586,22 @@ void * srt::CUDT::tsbpd(void* param) * - Closing the connection */ HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: no data, scheduling wakeup at ack"); - self->m_bTsbPdAckWakeup = true; + self->m_bTsbPdNeedsWakeup = true; THREAD_PAUSED(); tsbpd_cc.wait(); THREAD_RESUMED(); } - HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: WAKE UP!!!"); + HLOGC(tslog.Debug, + log << self->CONID() << "tsbpd: WAKE UP [" << (bWokeUpOnSignal ? "signal" : "timeout") << "]!!! - " + << "NOW=" << FormatTime(steady_clock::now())); } THREAD_EXIT(); HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: EXITING"); return NULL; } -int srt::CUDT::rcvDropTooLateUpTo(int seqno) +int srt::CUDT::rcvDropTooLateUpTo(int seqno, DropReason reason) { // Make sure that it would not drop over m_iRcvCurrSeqNo, which may break senders. if (CSeqNo::seqcmp(seqno, CSeqNo::incseq(m_iRcvCurrSeqNo)) > 0) @@ -5576,16 +5609,22 @@ int srt::CUDT::rcvDropTooLateUpTo(int seqno) dropFromLossLists(SRT_SEQNO_NONE, CSeqNo::decseq(seqno)); - const int iDropCnt = m_pRcvBuffer->dropUpTo(seqno); - if (iDropCnt > 0) + const std::pair iDropDiscardedPkts = m_pRcvBuffer->dropUpTo(seqno); + const int iDropCnt = iDropDiscardedPkts.first; + const int iDiscardedCnt = iDropDiscardedPkts.second; + const int iDropCntTotal = iDropCnt + iDiscardedCnt; + + // In case of DROP_TOO_LATE discarded packets should also be counted because they are not read from another member socket. + const int iDropStatCnt = (reason == DROP_DISCARD) ? iDropCnt : iDropCntTotal; + if (iDropStatCnt > 0) { enterCS(m_StatsLock); // Estimate dropped bytes from average payload size. const uint64_t avgpayloadsz = m_pRcvBuffer->getRcvAvgPayloadSize(); - m_stats.rcvr.dropped.count(stats::BytesPackets(iDropCnt * avgpayloadsz, (uint32_t) iDropCnt)); + m_stats.rcvr.dropped.count(stats::BytesPackets(iDropStatCnt * avgpayloadsz, (uint32_t)iDropStatCnt)); leaveCS(m_StatsLock); } - return iDropCnt; + return iDropCntTotal; } void srt::CUDT::setInitialRcvSeq(int32_t isn) @@ -5655,6 +5694,14 @@ bool srt::CUDT::prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd return true; } +int srt::CUDT::getAuthTagSize() const +{ + if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + return HAICRYPT_AUTHTAG_MAX; + + return 0; +} + bool srt::CUDT::prepareBuffers(CUDTException* eout) { if (m_pSndBuffer) @@ -5666,7 +5713,7 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) try { // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. - const int authtag = (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) ? HAICRYPT_AUTHTAG_MAX : 0; + const int authtag = getAuthTagSize(); SRT_ASSERT(m_iMaxSRTPayloadSize != 0); @@ -5801,6 +5848,16 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& throw CUDTException(MJ_SETUP, MN_REJECTED, 0); } +#if ENABLE_BONDING + // The socket and the group are only linked to each other after interpretSrtHandshake(..) has been called. + // Keep the group alive for the lifetime of this function, + // and do it BEFORE acquiring m_ConnectionLock to avoid + // lock inversion. + // This will check if a socket belongs to a group and if so + // it will remember this group and keep it alive here. + CUDTUnited::GroupKeeper group_keeper(uglobal(), m_parent); +#endif + if (!prepareBuffers(NULL)) { HLOGC(cnlog.Debug, @@ -5820,8 +5877,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& { #if ENABLE_BONDING - ScopedLock cl (uglobal().m_GlobControlLock); - CUDTGroup* g = m_parent->m_GroupOf; + CUDTGroup* g = group_keeper.group; if (g) { // This is the last moment when this can be done. @@ -5868,15 +5924,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); @@ -5890,10 +5946,10 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // To make sure what REALLY is being sent, parse back the handshake // data that have been just written into the buffer. CHandShake debughs; - debughs.load_from(response.m_pcData, response.getLength()); + debughs.load_from(rsppkt.m_pcData, rsppkt.getLength()); HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: sending HS from agent @" - << debughs.m_iID << " to peer @" << response.m_iID + << debughs.m_iID << " to peer @" << rsppkt.id() << "HS:" << debughs.show() << " sourceIP=" << m_SourceAddr.str()); } @@ -5904,7 +5960,7 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // When missed this message, the caller should not accept packets // coming as connected, but continue repeated handshake until finally // received the listener's handshake. - addressAndSend((response)); + addressAndSend((rsppkt)); } bool srt::CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::string& w_why) @@ -5987,7 +6043,7 @@ SRT_REJECT_REASON srt::CUDT::setupCC() // Create the CCC object and configure it. // UDT also sets back the congestion window: ??? - // m_dCongestionWindow = m_pCC->m_dCWndSize; + // m_iCongestionWindow = m_pCC->m_dCWndSize; // XXX Not sure about that. May happen that AGENT wants // tsbpd mode, but PEER doesn't, even in bidirectional mode. @@ -6150,7 +6206,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, @@ -6161,8 +6217,9 @@ void srt::CUDT::addressAndSend(CPacket& w_pkt) m_pSndQueue->sendto(m_PeerAddr, w_pkt, m_SourceAddr); } -// [[using maybe_locked(m_GlobControlLock, if called from GC)]] -bool srt::CUDT::closeInternal() +// [[using maybe_locked(m_GlobControlLock, if called from breakSocket_LOCKED, usually from GC)]] +// [[using maybe_locked(m_parent->m_ControlLock, if called from srt_close())]] +bool srt::CUDT::closeInternal() ATR_NOEXCEPT { // NOTE: this function is called from within the garbage collector thread. @@ -6921,6 +6978,12 @@ bool srt::CUDT::isRcvBufferReadyNoLock() const return m_pRcvBuffer->isRcvDataReady(steady_clock::now()); } +bool srt::CUDT::isRcvBufferFull() const +{ + ScopedLock lck(m_RcvBufferLock); + return m_pRcvBuffer->full(); +} + // int by_exception: accepts values of CUDTUnited::ErrorHandling: // - 0 - by return value // - 1 - by exception @@ -7515,7 +7578,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) perf->mbpsRecvRate = double(perf->byteRecv) * 8.0 / interval; perf->usPktSndPeriod = (double) count_microseconds(m_tdSendInterval.load()); perf->pktFlowWindow = m_iFlowWindowSize.load(); - perf->pktCongestionWindow = (int)m_dCongestionWindow; + perf->pktCongestionWindow = m_iCongestionWindow; perf->pktFlightSize = flight_span; perf->msRTT = (double)m_iSRTT / 1000.0; perf->msSndTsbPdDelay = m_bPeerTsbPd ? m_iPeerTsbPdDelay_ms : 0; @@ -7704,12 +7767,13 @@ bool srt::CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) // - m_dPktSndPeriod // - m_dCWndSize m_tdSendInterval = microseconds_from((int64_t)m_CongCtl->pktSndPeriod_us()); - m_dCongestionWindow = m_CongCtl->cgWindowSize(); + const double cgwindow = m_CongCtl->cgWindowSize(); + m_iCongestionWindow = cgwindow; #if ENABLE_HEAVY_LOGGING HLOGC(rslog.Debug, - log << CONID() << "updateCC: updated values from congctl: interval=" << count_microseconds(m_tdSendInterval) << " us (" - << "tk (" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" - << std::setprecision(3) << m_dCongestionWindow); + log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) + << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" + << std::setprecision(3) << cgwindow); #endif } @@ -7787,40 +7851,6 @@ void srt::CUDT::releaseSynch() leaveCS(m_RecvLock); } - -#if ENABLE_BONDING -void srt::CUDT::dropToGroupRecvBase() -{ - int32_t group_recv_base = SRT_SEQNO_NONE; - if (m_parent->m_GroupOf) - { - // Check is first done before locking to avoid unnecessary - // mutex locking. The condition for this field is that it - // can be either never set, already reset, or ever set - // and possibly dangling. The re-check after lock eliminates - // the dangling case. - ScopedLock glock (uglobal().m_GlobControlLock); - - // Note that getRcvBaseSeqNo() will lock m_GroupOf->m_GroupLock, - // but this is an intended order. - if (m_parent->m_GroupOf) - group_recv_base = m_parent->m_GroupOf->getRcvBaseSeqNo(); - } - if (group_recv_base == SRT_SEQNO_NONE) - return; - - ScopedLock lck(m_RcvBufferLock); - int cnt = rcvDropTooLateUpTo(CSeqNo::incseq(group_recv_base)); - if (cnt > 0) - { - HLOGC(grlog.Debug, - log << CONID() << "dropToGroupRecvBase: dropped " << cnt << " packets before ACK: group_recv_base=" - << group_recv_base << " m_iRcvLastAck=" << m_iRcvLastAck - << " m_iRcvCurrSeqNo=" << m_iRcvCurrSeqNo << " m_bTsbPd=" << m_bTsbPd); - } -} -#endif - namespace srt { #if ENABLE_HEAVY_LOGGING static void DebugAck(string hdr, int prev, int ack) @@ -7871,7 +7901,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_ACKACK: // 110 - Acknowledgement of Acknowledgement ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7886,7 +7916,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp size_t bytes = sizeof(*lossdata) * size; ctrlpkt.pack(pkttype, NULL, lossdata, bytes); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7907,7 +7937,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (0 < losslen) { ctrlpkt.pack(pkttype, NULL, data, losslen * 4); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); enterCS(m_StatsLock); @@ -7937,7 +7967,7 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_CGWARNING: // 100 - Congestion Warning ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); m_tsLastWarningTime = steady_clock::now(); @@ -7946,14 +7976,14 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp case UMSG_KEEPALIVE: // 001 - Keep-alive ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_HANDSHAKE: // 000 - Handshake ctrlpkt.pack(pkttype, NULL, rparam, sizeof(CHandShake)); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -7962,21 +7992,21 @@ void srt::CUDT::sendCtrl(UDTMessageType pkttype, const int32_t* lparam, void* rp if (m_PeerID == 0) // Dont't send SHUTDOWN if we don't know peer ID. break; ctrlpkt.pack(pkttype); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_DROPREQ: // 111 - Msg drop request ctrlpkt.pack(pkttype, lparam, rparam, 8); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; case UMSG_PEERERROR: // 1000 - acknowledge the peer side a special error ctrlpkt.pack(pkttype, lparam); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); break; @@ -8037,10 +8067,6 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) #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); @@ -8063,7 +8089,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) { bufflock.unlock(); ctrlpkt.pack(UMSG_ACK, NULL, &ack, size); - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(lite): ", local_prevack, ack); return nbsent; @@ -8148,7 +8174,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) /* Newly acknowledged data, signal TsbPD thread */ CUniqueSync tslcc (m_RecvLock, m_RcvTsbPdCond); // m_bTsbPdAckWakeup is protected by m_RecvLock in the tsbpd() thread - if (m_bTsbPdAckWakeup) + if (m_bTsbPdNeedsWakeup) tslcc.notify_one(); } else @@ -8211,7 +8237,8 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) else if (!bNeedFullAck) { // Not possible (m_iRcvCurrSeqNo+1 <% m_iRcvLastAck ?) - LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr %" << ack << " <% last %" << m_iRcvLastAck); + LOGC(xtlog.Error, log << CONID() << "sendCtrl(UMSG_ACK): IPE: curr(" << reason << ") %" + << ack << " <% last %" << m_iRcvLastAck); return nbsent; } @@ -8267,7 +8294,7 @@ int srt::CUDT::sendCtrlAck(CPacket& ctrlpkt, int size) ctrlpkt.pack(UMSG_ACK, &m_iAckSeqNo, data, ACKD_FIELD_SIZE * ACKD_TOTAL_SIZE_SMALL); } - ctrlpkt.m_iID = m_PeerID; + ctrlpkt.set_id(m_PeerID); setPacketTS(ctrlpkt, steady_clock::now()); nbsent = m_pSndQueue->sendto(m_PeerAddr, ctrlpkt, m_SourceAddr); DebugAck(CONID() + "sendCtrl(UMSG_ACK): ", local_prevack, ack); @@ -8452,7 +8479,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ if (CSeqNo::seqcmp(ackdata_seqno, m_iSndLastAck) >= 0) { - const int cwnd1 = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd1 = std::min(m_iFlowWindowSize, m_iCongestionWindow); const bool bWasStuck = cwnd1<= getFlightSpan(); // Update Flow Window Size, must update before and together with m_iSndLastAck m_iFlowWindowSize = ackdata[ACKD_BUFFERLEFT]; @@ -8460,7 +8487,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ m_tsLastRspAckTime = currtime; m_iReXmitCount = 1; // Reset re-transmit count since last ACK - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd = std::min(m_iFlowWindowSize, m_iCongestionWindow); if (bWasStuck && cwnd > getFlightSpan()) { m_pSndQueue->m_pSndUList->update(this, CSndUList::DONT_RESCHEDULE); @@ -8702,16 +8729,15 @@ void srt::CUDT::processCtrlAckAck(const CPacket& ctrlpkt, const time_point& tsAr // srt_recvfile (which doesn't make any sense), you'll have a deadlock. if (m_config.bDriftTracer) { - const bool drift_updated SRT_ATR_UNUSED = m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); +#if ENABLE_BONDING + ScopedLock glock(uglobal().m_GlobControlLock); + const bool drift_updated = +#endif + m_pRcvBuffer->addRcvTsbPdDriftSample(ctrlpkt.getMsgTimeStamp(), tsArrival, rtt); + #if ENABLE_BONDING if (drift_updated && m_parent->m_GroupOf) - { - ScopedLock glock(uglobal().m_GlobControlLock); - if (m_parent->m_GroupOf) - { - m_parent->m_GroupOf->synchronizeDrift(this); - } - } + m_parent->m_GroupOf->synchronizeDrift(this); #endif } @@ -8965,20 +8991,20 @@ void srt::CUDT::processCtrlHS(const CPacket& ctrlpkt) log << CONID() << "processCtrl: responding HS reqtype=" << RequestTypeStr(initdata.m_iReqType) << (have_hsreq ? " WITH SRT HS response extensions" : "")); - CPacket response; - response.setControl(UMSG_HANDSHAKE); - response.allocate(m_iMaxSRTPayloadSize); + CPacket rsppkt; + rsppkt.setControl(UMSG_HANDSHAKE); + rsppkt.allocate(m_iMaxSRTPayloadSize); // If createSrtHandshake failed, don't send anything. Actually it can only fail on IPE. // There is also no possible IPE condition in case of HSv4 - for this version it will always return true. enterCS(m_ConnectionLock); - bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (response), (initdata)); + bool create_ok = createSrtHandshake(SRT_CMD_HSRSP, SRT_CMD_KMRSP, kmdata, kmdatasize, (rsppkt), (initdata)); leaveCS(m_ConnectionLock); if (create_ok) { - response.m_iID = m_PeerID; - setPacketTS(response, steady_clock::now()); - const int nbsent = m_pSndQueue->sendto(m_PeerAddr, response, m_SourceAddr); + rsppkt.set_id(m_PeerID); + setPacketTS(rsppkt, steady_clock::now()); + const int nbsent = m_pSndQueue->sendto(m_PeerAddr, rsppkt, m_SourceAddr); if (nbsent) { m_tsLastSndTime.store(steady_clock::now()); @@ -9110,7 +9136,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()) { @@ -9285,37 +9311,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; } @@ -9325,35 +9358,34 @@ 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; + typedef CSndBuffer::DropRange DropRange; + + DropRange buffer_drop; steady_clock::time_point tsOrigin; - const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (msglen)); - if (payload == -1) + const int payload = m_pSndBuffer->readData(offset, (w_packet), (tsOrigin), (buffer_drop)); + if (payload == CSndBuffer::READ_DROP) { - int32_t seqpair[2]; - seqpair[0] = w_packet.m_iSeqNo; - SRT_ASSERT(msglen >= 1); - seqpair[1] = CSeqNo::incseq(seqpair[0], msglen - 1); + SRT_ASSERT(CSeqNo::seqoff(buffer_drop.seqno[DropRange::BEGIN], buffer_drop.seqno[DropRange::END]) >= 0); HLOGC(qrlog.Debug, - log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: msgno=" - << MSGNO_SEQ::unwrap(w_packet.m_iMsgNo) << " msglen=" << msglen << " SEQ:" << seqpair[0] << " - " - << seqpair[1]); - sendCtrl(UMSG_DROPREQ, &w_packet.m_iMsgNo, seqpair, sizeof(seqpair)); + log << CONID() << "loss-reported packets expired in SndBuf - requesting DROP: #" + << buffer_drop.msgno << " %(" << buffer_drop.seqno[DropRange::BEGIN] << " - " + << buffer_drop.seqno[DropRange::END] << ")"); + sendCtrl(UMSG_DROPREQ, &buffer_drop.msgno, buffer_drop.seqno, sizeof(buffer_drop.seqno)); // skip all dropped packets - m_pSndLossList->removeUpTo(seqpair[1]); - m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, seqpair[1]); + m_pSndLossList->removeUpTo(buffer_drop.seqno[DropRange::END]); + m_iSndCurrSeqNo = CSeqNo::maxseq(m_iSndCurrSeqNo, buffer_drop.seqno[DropRange::END]); continue; } - else if (payload == 0) + else if (payload == CSndBuffer::READ_NONE) continue; // The packet has been ecrypted, thus the authentication tag is expected to be stored @@ -9378,7 +9410,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); @@ -9483,7 +9515,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) @@ -9495,14 +9527,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) @@ -9511,7 +9543,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) @@ -9622,14 +9654,14 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime new_packet_packed = true; // every 16 (0xF) packets, a packet pair is sent - if ((w_packet.m_iSeqNo & PUMASK_SEQNO_PROBE) == 0) + if ((w_packet.seqno() & PUMASK_SEQNO_PROBE) == 0) probe = true; payload = (int) w_packet.getLength(); IF_HEAVY_LOGGING(reason = "normal"); } - w_packet.m_iID = m_PeerID; // Set the destination SRT socket ID. + w_packet.set_id(m_PeerID); // Set the destination SRT socket ID. if (new_packet_packed && m_PacketFilter) { @@ -9639,7 +9671,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime #if ENABLE_HEAVY_LOGGING // Required because of referring to MessageFlagStr() HLOGC(qslog.Debug, - log << CONID() << "packData: " << reason << " packet seq=" << w_packet.m_iSeqNo << " (ACK=" << m_iSndLastAck + log << CONID() << "packData: " << reason << " packet seq=" << w_packet.seqno() << " (ACK=" << m_iSndLastAck << " ACKDATA=" << m_iSndLastDataAck << " MSG/FLAGS: " << w_packet.MessageFlagStr() << ")"); #endif @@ -9658,7 +9690,7 @@ bool srt::CUDT::packData(CPacket& w_packet, steady_clock::time_point& w_nexttime // Left untouched for historical reasons. // Might be possible that it was because of that this is send from // different thread than the rest of the signals. - // m_pSndTimeWindow->onPktSent(w_packet.m_iTimeStamp); + // m_pSndTimeWindow->onPktSent(w_packet.timestamp()); enterCS(m_StatsLock); m_stats.sndr.sent.count(payload); @@ -9715,12 +9747,12 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { ScopedLock lkrack (m_RecvAckLock); // Check the congestion/flow window limit - const int cwnd = std::min(int(m_iFlowWindowSize), int(m_dCongestionWindow)); + const int cwnd = std::min(m_iFlowWindowSize, m_iCongestionWindow); const int flightspan = getFlightSpan(); if (cwnd <= flightspan) { HLOGC(qslog.Debug, - log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_dCongestionWindow + log << CONID() << "packUniqueData: CONGESTED: cwnd=min(" << m_iFlowWindowSize << "," << m_iCongestionWindow << ")=" << cwnd << " seqlen=(" << m_iSndLastAck << "-" << m_iSndCurrSeqNo << ")=" << flightspan); return false; } @@ -9758,7 +9790,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Fortunately the group itself isn't being accessed. if (m_parent->m_GroupOf) { - const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.m_iSeqNo); + const int packetspan = CSeqNo::seqoff(current_sequence_number, w_packet.seqno()); if (packetspan > 0) { // After increasing by 1, but being previously set as ISN-1, this should be == ISN, @@ -9772,7 +9804,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // initialized from ISN just after connection. LOGC(qslog.Note, log << CONID() << "packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " for the first packet: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " for the first packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } else @@ -9780,7 +9812,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // There will be a serious data discrepancy between the agent and the peer. LOGC(qslog.Error, log << CONID() << "IPE: packUniqueData: Fixing EXTRACTION sequence " << current_sequence_number - << " from SCHEDULING sequence " << w_packet.m_iSeqNo << " in the middle of transition: DIFF=" + << " from SCHEDULING sequence " << w_packet.seqno() << " in the middle of transition: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); } @@ -9789,7 +9821,7 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Don't do it if the difference isn't positive or exceeds the threshold. int32_t seqpair[2]; seqpair[0] = current_sequence_number; - seqpair[1] = CSeqNo::decseq(w_packet.m_iSeqNo); + seqpair[1] = CSeqNo::decseq(w_packet.seqno()); const int32_t no_msgno = 0; LOGC(qslog.Debug, log << CONID() << "packUniqueData: Sending DROPREQ: SEQ: " << seqpair[0] << " - " << seqpair[1] << " (" @@ -9801,16 +9833,16 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) // Override extraction sequence with scheduling sequence. ScopedLock ackguard(m_RecvAckLock); - m_iSndCurrSeqNo = w_packet.m_iSeqNo; - m_iSndLastAck = w_packet.m_iSeqNo; - m_iSndLastDataAck = w_packet.m_iSeqNo; - m_iSndLastFullAck = w_packet.m_iSeqNo; - m_iSndLastAck2 = w_packet.m_iSeqNo; + m_iSndCurrSeqNo = w_packet.seqno(); + m_iSndLastAck = w_packet.seqno(); + m_iSndLastDataAck = w_packet.seqno(); + m_iSndLastFullAck = w_packet.seqno(); + m_iSndLastAck2 = w_packet.seqno(); } else if (packetspan < 0) { LOGC(qslog.Error, - log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.m_iSeqNo + log << CONID() << "IPE: packData: SCHEDULING sequence " << w_packet.seqno() << " is behind of EXTRACTION sequence " << current_sequence_number << ", dropping this packet: DIFF=" << packetspan << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // XXX: Probably also change the socket state to broken? @@ -9822,15 +9854,15 @@ bool srt::CUDT::packUniqueData(CPacket& w_packet) { HLOGC(qslog.Debug, log << CONID() << "packUniqueData: Applying EXTRACTION sequence " << current_sequence_number - << " over SCHEDULING sequence " << w_packet.m_iSeqNo << " for socket not in group:" - << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.m_iSeqNo) + << " over SCHEDULING sequence " << w_packet.seqno() << " for socket not in group:" + << " DIFF=" << CSeqNo::seqcmp(current_sequence_number, w_packet.seqno()) << " STAMP=" << BufferStamp(w_packet.m_pcData, w_packet.getLength())); // Do this always when not in a group. - w_packet.m_iSeqNo = current_sequence_number; + w_packet.set_seqno(current_sequence_number); } // Set missing fields before encrypting the packet, because those fields might be used for encryption. - w_packet.m_iID = m_PeerID; // Destination SRT Socket ID + w_packet.set_id(m_PeerID); // Destination SRT Socket ID setDataPacketTS(w_packet, tsOrigin); if (kflg != EK_NOENC) @@ -9850,7 +9882,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 @@ -10019,7 +10051,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"); @@ -10044,7 +10076,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); @@ -10057,7 +10089,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; @@ -10079,7 +10111,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) @@ -10090,7 +10122,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()) ); @@ -10160,7 +10192,7 @@ int srt::CUDT::handleSocketPacketReception(const vector& incoming, bool& #endif } } - else if (m_pCryptoControl && m_pCryptoControl->getCryptoMode() == CSrtConfig::CIPHER_MODE_AES_GCM) + else if (m_pCryptoControl && m_pCryptoControl->m_RcvKmState == SRT_KM_S_SECURED) { // Unencrypted packets are not allowed. const int iDropCnt = m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE, CRcvBuffer::DROP_EXISTING); @@ -10209,7 +10241,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] @@ -10223,12 +10255,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); } @@ -10236,9 +10268,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 { @@ -10286,7 +10318,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"; @@ -10330,7 +10362,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, @@ -10356,7 +10388,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) @@ -10372,13 +10404,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(); } } @@ -10444,7 +10476,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), @@ -10729,7 +10761,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. @@ -10749,7 +10781,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); @@ -10964,7 +10996,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"); @@ -11052,7 +11084,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 @@ -11120,7 +11152,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 @@ -11168,8 +11200,8 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) hs.m_iReqType = URQFailure(m_RejectReason); size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); - packet.m_iID = id; - setPacketTS(packet, steady_clock::now()); + packet.set_id(id); + setPacketTS((packet), steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (e): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); } @@ -11280,7 +11312,7 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) CPacket rsp; setPacketTS((rsp), steady_clock::now()); rsp.pack(UMSG_SHUTDOWN); - rsp.m_iID = m_PeerID; + rsp.set_id(m_PeerID); m_pSndQueue->sendto(addr, rsp, use_source_addr); } else @@ -11291,14 +11323,14 @@ int srt::CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) size_t size = CHandShake::m_iContentSize; hs.store_to((packet.m_pcData), (size)); packet.setLength(size); - packet.m_iID = id; + packet.set_id(id); setPacketTS(packet, steady_clock::now()); HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: SENDING HS (a): " << hs.show()); m_pSndQueue->sendto(addr, packet, use_source_addr); } } } - LOGC(cnlog.Note, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); + LOGC(cnlog.Debug, log << CONID() << "listen ret: " << hs.m_iReqType << " - " << RequestTypeStr(hs.m_iReqType)); return RejectReasonForURQ(hs.m_iReqType); } diff --git a/srtcore/core.h b/srtcore/core.h index 5b4b27aaf..e24bd8152 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -120,7 +120,12 @@ enum AckDataItem }; const size_t ACKD_FIELD_SIZE = sizeof(int32_t); +#ifdef ENABLE_MAXREXMITBW +static const size_t SRT_SOCKOPT_NPOST = 13; +#else static const size_t SRT_SOCKOPT_NPOST = 12; +#endif + extern const SRT_SOCKOPT srt_post_opt_list []; enum GroupDataItem @@ -322,6 +327,7 @@ class CUDT #endif int32_t rcvSeqNo() const { return m_iRcvCurrSeqNo; } + SRT_ATTR_REQUIRES(m_RecvAckLock) int flowWindowSize() const { return m_iFlowWindowSize; } int32_t deliveryRate() const { return m_iDeliveryRate; } int bandwidth() const { return m_iBandwidth; } @@ -383,6 +389,7 @@ class CUDT /// Returns the number of packets in flight (sent, but not yet acknowledged). /// @returns The number of packets in flight belonging to the interval [0; ...) + SRT_ATTR_REQUIRES(m_RecvAckLock) int32_t getFlightSpan() const { return getFlightSpan(m_iSndLastAck, m_iSndCurrSeqNo); @@ -409,7 +416,7 @@ class CUDT static void setPacketTS(CPacket& p, const time_point& start_time, const time_point& ts) { - p.m_iTimeStamp = makeTS(ts, start_time); + p.set_timestamp(makeTS(ts, start_time)); } /// @brief Set the timestamp field of the packet using the provided value (no check) @@ -517,6 +524,7 @@ class CUDT /// Allocates sender and receiver buffers and loss lists. SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) bool prepareBuffers(CUDTException* eout); + int getAuthTagSize() const; SRT_ATR_NODISCARD SRT_ATTR_REQUIRES(m_ConnectionLock) EConnectStatus postConnect(const CPacket* response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT; @@ -596,7 +604,7 @@ class CUDT /// Close the opened UDT entity. - bool closeInternal(); + bool closeInternal() ATR_NOEXCEPT; void updateBrokenConnection(); void completeBrokenConnectionDependencies(int errorcode); @@ -691,6 +699,8 @@ class CUDT /// the receiver fresh loss list. void unlose(const CPacket& oldpacket); void dropFromLossLists(int32_t from, int32_t to); + + SRT_ATTR_REQUIRES(m_RecvAckLock) bool getFirstNoncontSequence(int32_t& w_seq, std::string& w_log_reason); SRT_ATTR_EXCLUDES(m_ConnectionLock) @@ -746,14 +756,24 @@ class CUDT SRT_ATTR_REQUIRES(m_RcvBufferLock) bool isRcvBufferReadyNoLock() const; + SRT_ATTR_EXCLUDES(m_RcvBufferLock) + bool isRcvBufferFull() const; + // TSBPD thread main function. static void* tsbpd(void* param); + enum DropReason + { + DROP_TOO_LATE, //< Drop to keep up to the live pace (TLPKTDROP). + DROP_DISCARD //< Drop because another group member already provided these packets. + }; + /// Drop too late packets (receiver side). Update loss lists and ACK positions. /// The @a seqno packet itself is not dropped. /// @param seqno [in] The sequence number of the first packets following those to be dropped. + /// @param reason A reason for dropping (see @a DropReason). /// @return The number of packets dropped. - int rcvDropTooLateUpTo(int seqno); + int rcvDropTooLateUpTo(int seqno, DropReason reason = DROP_TOO_LATE); static loss_seqs_t defaultPacketArrival(void* vself, CPacket& pkt); static loss_seqs_t groupPacketArrival(void* vself, CPacket& pkt); @@ -848,7 +868,7 @@ class CUDT SRT_ATTR_GUARDED_BY(m_RecvAckLock) sync::atomic m_iFlowWindowSize; // Flow control window size - double m_dCongestionWindow; // Congestion window size + sync::atomic m_iCongestionWindow; // Congestion window size private: // Timers atomic_time_point m_tsNextACKTime; // Next ACK time, in CPU clock cycles, same below @@ -974,7 +994,7 @@ class CUDT sync::CThread m_RcvTsbPdThread; // Rcv TsbPD Thread handle sync::Condition m_RcvTsbPdCond; // TSBPD signals if reading is ready. Use together with m_RecvLock - bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent + bool m_bTsbPdNeedsWakeup; // Signal TsbPd thread to wake up on RCV buffer state change. sync::Mutex m_RcvTsbPdStartupLock; // Protects TSBPD thread creating and joining CallbackHolder m_cbAcceptHook; @@ -1123,7 +1143,8 @@ class CUDT /// @return -2 The incoming packet exceeds the expected sequence by more than a length of the buffer (irrepairable discrepancy). int handleSocketPacketReception(const std::vector& incoming, bool& w_new_inserted, bool& w_was_sent_in_order, CUDT::loss_seqs_t& w_srt_loss_seqs); - /// Get the packet's TSBPD time. + /// Get the packet's TSBPD time - + /// the time when it is passed to the reading application. /// The @a grp passed by void* is not used yet /// and shall not be used when ENABLE_BONDING=0. time_point getPktTsbPdTime(void* grp, const CPacket& packet); @@ -1143,12 +1164,6 @@ class CUDT static void addLossRecord(std::vector& lossrecord, int32_t lo, int32_t hi); int32_t bake(const sockaddr_any& addr, int32_t previous_cookie = 0, int correction = 0); -#if ENABLE_BONDING - /// @brief Drop packets in the recv buffer behind group_recv_base. - /// Updates m_iRcvLastSkipAck if it's behind group_recv_base. - void dropToGroupRecvBase(); -#endif - void processKeepalive(const CPacket& ctrlpkt, const time_point& tsArrival); diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 9830a3bc8..fd762b88b 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -598,6 +598,9 @@ void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, g.flag_clip = g.flag_clip ^ kflg; g.timestamp_clip = g.timestamp_clip ^ timestamp_hw; + HLOGC(pflog.Debug, log << "FEC CLIP: data pkt.size=" << payload_size + << " to a clip buffer size=" << payloadSize()); + // Payload goes "as is". for (size_t i = 0; i < payload_size; ++i) { diff --git a/srtcore/group.cpp b/srtcore/group.cpp index b9b0a0d1c..3e57ef5df 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -259,6 +259,7 @@ CUDTGroup::CUDTGroup(SRT_GROUP_TYPE gtype) , m_uOPT_MinStabilityTimeout_us(1000 * CSrtConfig::COMM_DEF_MIN_STABILITY_TIMEOUT_MS) // -1 = "undefined"; will become defined with first added socket , m_iMaxPayloadSize(-1) + , m_iAvgPayloadSize(-1) , m_bSynRecving(true) , m_bSynSending(true) , m_bTsbPd(true) @@ -717,6 +718,16 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) return true; } +struct FOptionValue +{ + SRT_SOCKOPT expected; + FOptionValue(SRT_SOCKOPT v): expected(v) {} + bool operator()(const CUDTGroup::ConfigItem& i) const + { + return i.so == expected; + } +}; + void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) { // Options handled in group @@ -732,46 +743,60 @@ void CUDTGroup::getOpt(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) w_optlen = sizeof(bool); return; - default:; // pass on - } + case SRTO_SNDTIMEO: + *(int*)pw_optval = m_iSndTimeOut; + w_optlen = sizeof(int); + return; - // XXX Suspicous: may require locking of GlobControlLock - // to prevent from deleting a socket in the meantime. - // Deleting a socket requires removing from the group first, - // so after GroupLock this will be either already NULL or - // a valid socket that will only be closed after time in - // the GC, so this is likely safe like all other API functions. - CUDTSocket* ps = 0; + case SRTO_RCVTIMEO: + *(int*)pw_optval = m_iRcvTimeOut; + w_optlen = sizeof(int); + return; - { - // In sockets. All sockets should have all options - // set the same and should represent the group state - // well enough. If there are no sockets, just use default. + case SRTO_GROUPMINSTABLETIMEO: + *(uint32_t*)pw_optval = m_uOPT_MinStabilityTimeout_us / 1000; + w_optlen = sizeof(uint32_t); + return; - // Group lock to protect the container itself. - // Once a socket is extracted, we state it cannot be - // closed without the group send/recv function or closing - // being involved. - ScopedLock lg(m_GroupLock); - if (m_Group.empty()) - { - if (!getOptDefault(optname, (pw_optval), (w_optlen))) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Write-only options for security reasons or + // options that refer to a socket state, that + // makes no sense for a group. + case SRTO_PASSPHRASE: + case SRTO_KMSTATE: + case SRTO_PBKEYLEN: + case SRTO_KMPREANNOUNCE: + case SRTO_KMREFRESHRATE: + case SRTO_BINDTODEVICE: + case SRTO_GROUPCONNECT: + case SRTO_STATE: + case SRTO_EVENT: + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - return; - } + default:; // pass on + } + + // Check if the option is in the storage, which means that + // it was modified on the group. + + vector::const_iterator i = find_if(m_config.begin(), m_config.end(), + FOptionValue(optname)); - ps = m_Group.begin()->ps; + if (i == m_config.end()) + { + // Not found, see the defaults + if (!getOptDefault(optname, (pw_optval), (w_optlen))) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - // Release the lock on the group, as it's not necessary, - // as well as it might cause a deadlock when combined - // with the others. + return; } - if (!ps) - throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + // Found, return the value from the storage. + // Check the size first. + if (w_optlen < int(i->value.size())) + throw CUDTException(MJ_NOTSUP, MN_XSIZE, 0); - return ps->core().getOpt(optname, (pw_optval), (w_optlen)); + w_optlen = i->value.size(); + memcpy((pw_optval), &i->value[0], i->value.size()); } SRT_SOCKSTATUS CUDTGroup::getStatus() @@ -845,18 +870,9 @@ void CUDTGroup::syncWithSocket(const CUDT& core, const HandshakeSide side) set_currentSchedSequence(core.ISN()); } - // XXX - // Might need further investigation as to whether this isn't - // wrong for some cases. By having this -1 here the value will be - // laziliy set from the first reading one. It is believed that - // it covers all possible scenarios, that is: - // - // - no readers - no problem! - // - have some readers and a new is attached - this is set already - // - connect multiple links, but none has read yet - you'll be the first. - // - // Previous implementation used setting to: core.m_iPeerISN - resetInitialRxSequence(); + // Only set if was not initialized to avoid problems on a running connection. + if (m_RcvBaseSeqNo == SRT_SEQNO_NONE) + m_RcvBaseSeqNo = CSeqNo::decseq(core.m_iPeerISN); // Get the latency (possibly fixed against the opposite side) // from the first socket (core.m_iTsbPdDelay_ms), @@ -2024,10 +2040,14 @@ vector CUDTGroup::recv_WaitForReadReady(const vector& } else { - // No read-readiness reported by epoll, but probably missed or not yet handled - // as the receiver buffer is read-ready. + // No read-readiness reported by epoll, but can be missed or not yet handled + // while the receiver buffer is in fact read-ready. ScopedLock lg(sock->core().m_RcvBufferLock); - if (sock->core().m_pRcvBuffer && sock->core().m_pRcvBuffer->isRcvDataReady()) + if (!sock->core().m_pRcvBuffer) + continue; + // Checking for the next packet in the RCV buffer is safer that isReadReady(tnow). + const CRcvBuffer::PacketInfo info = sock->core().m_pRcvBuffer->getFirstValidPacketInfo(); + if (info.seqno != SRT_SEQNO_NONE && !info.seq_gap) readReady.push_back(sock); } } @@ -2197,6 +2217,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } // Find the first readable packet among all member sockets. + steady_clock::time_point tnow = steady_clock::now(); CUDTSocket* socketToRead = NULL; CRcvBuffer::PacketInfo infoToRead = {-1, false, time_point()}; for (vector::const_iterator si = readySockets.begin(); si != readySockets.end(); ++si) @@ -2217,7 +2238,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } const CRcvBuffer::PacketInfo info = - ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(steady_clock::now()); + ps->core().m_pRcvBuffer->getFirstReadablePacketInfo(tnow); if (info.seqno == SRT_SEQNO_NONE) { HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": @" << ps->m_SocketID << ": Nothing to read."); @@ -2237,6 +2258,12 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) { socketToRead = ps; infoToRead = info; + + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE && ((CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) == 1)) + { + // We have the next packet. No need to check other read-ready sockets. + break; + } } } @@ -2285,6 +2312,20 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) } fillGroupData((w_mc), w_mc); + // m_RcvBaseSeqNo is expected to be set to the PeerISN with the first connected member, + // so a packet drop at the start should also be detected by this condition. + if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) + { + const int32_t iNumDropped = (CSeqNo(w_mc.pktseq) - CSeqNo(m_RcvBaseSeqNo)) - 1; + if (iNumDropped > 0) + { + m_stats.recvDrop.count(stats::BytesPackets(iNumDropped * static_cast(avgRcvPacketSize()), iNumDropped)); + LOGC(grlog.Warn, + log << "@" << m_GroupID << " GROUP RCV-DROPPED " << iNumDropped << " packet(s): seqno %" + << CSeqNo::incseq(m_RcvBaseSeqNo) << " to %" << CSeqNo::decseq(w_mc.pktseq)); + } + } + HLOGC(grlog.Debug, log << "grp/recv: $" << id() << ": Update m_RcvBaseSeqNo: %" << m_RcvBaseSeqNo << " -> %" << w_mc.pktseq); m_RcvBaseSeqNo = w_mc.pktseq; @@ -2300,7 +2341,7 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) ScopedLock lg(ps->core().m_RcvBufferLock); if (m_RcvBaseSeqNo != SRT_SEQNO_NONE) { - const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo)); + const int cnt = ps->core().rcvDropTooLateUpTo(CSeqNo::incseq(m_RcvBaseSeqNo), CUDT::DROP_DISCARD); if (cnt > 0) { HLOGC(grlog.Debug, @@ -2533,7 +2574,7 @@ class StabilityTracer str_tnow.replace(str_tnow.find(':'), 1, 1, '_'); } const std::string fname = "stability_trace_" + str_tnow + ".csv"; - m_fout.open(fname, std::ofstream::out); + m_fout.open(fname.c_str(), std::ofstream::out); if (!m_fout) std::cerr << "IPE: Failed to open " << fname << "!!!\n"; diff --git a/srtcore/list.cpp b/srtcore/list.cpp index 0a175ee9d..0fe7d2b4b 100644 --- a/srtcore/list.cpp +++ b/srtcore/list.cpp @@ -653,6 +653,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } @@ -708,6 +710,8 @@ bool srt::CRcvLossList::remove(int32_t seqno) } m_iLength--; + if (m_iLength == 0) + m_iLargestSeq = SRT_SEQNO_NONE; return true; } diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index d309b1b8a..d0ba3fd4a 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -14,7 +14,7 @@ written by *****************************************************************************/ - +#include "srt_compat.h" #include "logging.h" using namespace std; @@ -23,69 +23,6 @@ using namespace std; namespace srt_logging { -// Note: subscribe() and unsubscribe() functions are being called -// in the global constructor and destructor only, as the -// Logger objects (and inside them also their LogDispatcher) -// are being created. It's not predicted that LogDispatcher -// object are going to be created any other way than as -// global objects. Therefore the construction and destruction -// of them happens always in the main thread. - -void LogConfig::subscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - return; // Do not register twice - - loggers.push_back(lg); -} - -void LogConfig::unsubscribe(LogDispatcher* lg) -{ - vector::iterator p = std::find(loggers.begin(), loggers.end(), lg); - if (p != loggers.end()) - { - loggers.erase(p); - } -} - -// This function doesn't have any protection on itself, -// however the API functions from which it is called, call -// it already under a mutex protection. -void LogConfig::updateLoggersState() -{ - for (vector::iterator p = loggers.begin(); - p != loggers.end(); ++p) - { - (*p)->Update(); - } -} - -void LogDispatcher::Update() -{ - bool enabled_in_fa = src_config->enabled_fa[fa]; - enabled = enabled_in_fa && level <= src_config->max_level; -} - - -// SendLogLine can be compiled normally. It's intermediately used by: -// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING -// - PrintLogLine, which has empty body when !ENABLE_LOGGING -void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) -{ - src_config->lock(); - if ( src_config->loghandler_fn ) - { - (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); - } - else if ( src_config->log_stream ) - { - (*src_config->log_stream) << msg; - src_config->log_stream->flush(); - } - src_config->unlock(); -} - #if ENABLE_LOGGING diff --git a/srtcore/logging.h b/srtcore/logging.h index 608234eab..2ec5f46aa 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,6 @@ written by #include #include #include -#include #include #include #ifdef _WIN32 @@ -34,7 +33,6 @@ written by #include "utilities.h" #include "threadname.h" #include "logging_api.h" -#include "srt_compat.h" #include "sync.h" #ifdef __GNUC__ @@ -116,7 +114,6 @@ struct LogConfig void* loghandler_opaque; srt::sync::Mutex mutex; int flags; - std::vector loggers; LogConfig(const fa_bitset_t& efa, LogLevel::type l = LogLevel::warning, @@ -139,10 +136,6 @@ struct LogConfig SRT_ATTR_RELEASE(mutex) void unlock() { mutex.unlock(); } - - void subscribe(LogDispatcher*); - void unsubscribe(LogDispatcher*); - void updateLoggersState(); }; // The LogDispatcher class represents the object that is responsible for @@ -154,7 +147,6 @@ struct SRT_API LogDispatcher LogLevel::type level; static const size_t MAX_PREFIX_SIZE = 32; char prefix[MAX_PREFIX_SIZE+1]; - bool enabled; LogConfig* src_config; bool isset(int flg) { return (src_config->flags & flg) != 0; } @@ -165,7 +157,6 @@ struct SRT_API LogDispatcher const char* logger_pfx /*[[nullable]]*/, LogConfig& config): fa(functional_area), level(log_level), - enabled(false), src_config(&config) { // XXX stpcpy desired, but not enough portable @@ -193,18 +184,13 @@ struct SRT_API LogDispatcher prefix[MAX_PREFIX_SIZE] = '\0'; #endif } - config.subscribe(this); - Update(); } ~LogDispatcher() { - src_config->unsubscribe(this); } - void Update(); - - bool CheckEnabled() { return enabled; } + bool CheckEnabled(); void CreateLogLinePrefix(std::ostringstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); @@ -217,16 +203,16 @@ struct SRT_API LogDispatcher template void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args); - template - void operator()(Arg1&& arg1, Args&&... args) + template + void operator()(Args&&... args) { - PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...); + PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", args...); } - template - void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args) + template + void printloc(const char* file, int line, const std::string& area, Args&&... args) { - PrintLogLine(file, line, area, arg1, args...); + PrintLogLine(file, line, area, args...); } #else template @@ -428,6 +414,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 @@ -438,7 +440,7 @@ inline void PrintArgs(std::ostream&) {} template inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) { - serr << arg1; + serr << std::forward(arg1); PrintArgs(serr, args...); } @@ -478,7 +480,24 @@ inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int lin #endif // HAVE_CXX11 +// SendLogLine can be compiled normally. It's intermediately used by: +// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING +// - PrintLogLine, which has empty body when !ENABLE_LOGGING +inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg) +{ + src_config->lock(); + if ( src_config->loghandler_fn ) + { + (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str()); + } + else if ( src_config->log_stream ) + { + (*src_config->log_stream) << msg; + (*src_config->log_stream).flush(); + } + src_config->unlock(); } -#endif // INC_SRT_LOGGING_H +} +#endif // INC_SRT_LOGGING_H diff --git a/srtcore/packet.cpp b/srtcore/packet.cpp index fbb56a42c..180623039 100644 --- a/srtcore/packet.cpp +++ b/srtcore/packet.cpp @@ -159,7 +159,7 @@ modified by // the original sequence numbers in the field. #include "platform_sys.h" - +#include #include #include "packet.h" #include "handshake.h" @@ -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(); @@ -436,38 +432,29 @@ void CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, } } -void CPacket::toNL() +void CPacket::toNetworkByteOrder() { - // XXX USE HtoNLA! + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t i = 0, n = getLength() / 4; i < n; ++i) - *((uint32_t*)m_pcData + i) = htonl(*((uint32_t*)m_pcData + i)); + HtoNLA((uint32_t*) m_pcData, (const uint32_t*) m_pcData, getLength() / 4); } - // convert packet header into network order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int j = 0; j < 4; ++j) - { - *p = htonl(*p); - ++p; - } + HtoNLA(p, p, 4); } -void CPacket::toHL() +void CPacket::toHostByteOrder() { - // convert back into local host order + // Convert packet header independent of packet type. uint32_t* p = m_nHeader; - for (int k = 0; k < 4; ++k) - { - *p = ntohl(*p); - ++p; - } + NtoHLA(p, p, 4); + // The payload of data packet should remain in network byte order. if (isControl()) { - for (ptrdiff_t l = 0, n = getLength() / 4; l < n; ++l) - *((uint32_t*)m_pcData + l) = ntohl(*((uint32_t*)m_pcData + l)); + NtoHLA((uint32_t*)m_pcData, (const uint32_t*)m_pcData, getLength() / 4); } } @@ -601,7 +588,7 @@ inline void SprintSpecialWord(std::ostream& os, int32_t val) std::string CPacket::Info() { std::ostringstream os; - os << "TARGET=@" << m_iID << " "; + os << "TARGET=@" << id() << " "; if (isControl()) { diff --git a/srtcore/packet.h b/srtcore/packet.h index 027d5f0b3..5094247b5 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -331,8 +331,10 @@ class CPacket }; public: - void toNL(); - void toHL(); + /// @brief Convert the packet inline to a network byte order (Little-endian). + void toNetworkByteOrder(); + /// @brief Convert the packet inline to a host byte order. + void toHostByteOrder(); protected: // DynamicStruct is the same as array of given type and size, just it @@ -352,12 +354,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 37785f43a..dc7e5b422 100644 --- a/srtcore/packetfilter.cpp +++ b/srtcore/packetfilter.cpp @@ -226,7 +226,7 @@ bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_pack // - Crypto // - Message Number // will be set to 0/false - w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + w_packet.set_msgflags(SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg)); @@ -314,9 +314,15 @@ bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::strin init.socket_id = parent->socketID(); init.snd_isn = parent->sndSeqNo(); init.rcv_isn = parent->rcvSeqNo(); - init.payload_size = parent->OPT_PayloadSize(); + + // XXX This is a formula for a full "SRT payload" part that undergoes transmission, + // might be nice to have this formula as something more general. + init.payload_size = parent->OPT_PayloadSize() + parent->getAuthTagSize(); init.rcvbuf_size = parent->m_config.iRcvBufSize; + HLOGC(pflog.Debug, log << "PFILTER: @" << init.socket_id << " payload size=" + << init.payload_size << " rcvbuf size=" << init.rcvbuf_size); + // Found a filter, so call the creation function m_filter = selector->second->Create(init, m_provided, confstr); if (!m_filter) diff --git a/srtcore/platform_sys.h b/srtcore/platform_sys.h index e2f0aa4d9..83763e5ea 100644 --- a/srtcore/platform_sys.h +++ b/srtcore/platform_sys.h @@ -21,6 +21,20 @@ // // SRT_IMPORT_TIME (mach time on Mac, portability gettimeofday on WIN32) // SRT_IMPORT_EVENT (includes kevent on Mac) +#ifdef _WIN32 + #ifndef __MINGW32__ + // Explicitly define 32-bit and 64-bit numbers + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + #ifndef LEGACY_WIN32 + typedef unsigned __int64 uint64_t; + #else + // VC 6.0 does not support unsigned __int64: may cause potential problems. + typedef __int64 uint64_t; + #endif + #endif +#endif #ifdef _WIN32 diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 2e01f4f71..f416ed25d 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -900,7 +900,7 @@ void srt::CRendezvousQueue::updateConnStatus(EReadStatus rst, EConnectStatus cst // Need a stub value for a case when there's no unit provided ("storage depleted" case). // It should be normally NOT IN USE because in case of "storage depleted", rst != RST_OK. - const SRTSOCKET dest_id = pkt ? pkt->m_iID : 0; + const SRTSOCKET dest_id = pkt ? pkt->id() : 0; // If no socket were qualified for further handling, finish here. // Otherwise toRemove and toProcess contain items to handle. @@ -1092,8 +1092,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(EReadStatus rst, if ((rst == RST_AGAIN || i->m_iID != iDstSockID) && tsNow <= tsRepeat) { HLOGC(cnlog.Debug, - log << "RID:@" << i->m_iID << std::fixed << count_microseconds(tsNow - tsLastReq) / 1000.0 - << " ms passed since last connection request."); + log << "RID:@" << i->m_iID << " " << FormatDuration(tsNow - tsLastReq) + << " passed since last connection request."); continue; } @@ -1385,7 +1385,7 @@ srt::EReadStatus srt::CRcvQueue::worker_RetrieveUnit(int32_t& w_id, CUnit*& w_un if (rst == RST_OK) { - w_id = w_unit->m_Packet.m_iID; + w_id = w_unit->m_Packet.id(); HLOGC(qrlog.Debug, log << "INCOMING PACKET: FROM=" << w_addr.str() << " BOUND=" << m_pChannel->bindAddressAny().str() << " " << w_unit->m_Packet.Info()); @@ -1407,7 +1407,7 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, ScopedLock cg(m_LSLock); if (m_pListener) { - LOGC(cnlog.Note, log << "PASSING request from: " << addr.str() << " to agent:" << m_pListener->socketID()); + LOGC(cnlog.Debug, log << "PASSING request from: " << addr.str() << " to listener:" << m_pListener->socketID()); listener_ret = m_pListener->processConnectRequest(addr, unit->m_Packet); // This function does return a code, but it's hard to say as to whether @@ -1426,8 +1426,8 @@ srt::EConnectStatus srt::CRcvQueue::worker_ProcessConnectionRequest(CUnit* unit, if (have_listener) // That is, the above block with m_pListener->processConnectRequest was executed { - LOGC(cnlog.Note, - log << CONID() << "Listener managed the connection request from: " << addr.str() + LOGC(cnlog.Debug, + log << CONID() << "Listener got the connection request from: " << addr.str() << " result:" << RequestTypeStr(UDTRequestType(listener_ret))); return listener_ret == SRT_REJ_UNKNOWN ? CONN_CONTINUE : CONN_REJECT; } @@ -1747,6 +1747,7 @@ void srt::CRcvQueue::setNewEntry(CUDT* u) bool srt::CRcvQueue::ifNewEntry() { + ScopedLock listguard(m_IDLock); return !(m_vNewEntry.empty()); } diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 8708e90a1..d44330f78 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -292,10 +292,8 @@ struct CSrtConfigSetter using namespace std; string val; - if (optlen == -1) - val = (const char *)optval; - else - val.assign((const char *)optval, optlen); + + val.assign((const char *)optval, optlen); if (val.size() >= IFNAMSIZ) { LOGC(kmlog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); @@ -597,10 +595,7 @@ struct CSrtConfigSetter static void set(CSrtConfig& co, const void* optval, int optlen) { std::string val; - if (optlen == -1) - val = (const char*)optval; - else - val.assign((const char*)optval, optlen); + val.assign((const char*)optval, optlen); // Translate alias if (val == "vod") diff --git a/srtcore/srt.h b/srtcore/srt.h index 53b6fd274..614a85aea 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -16,6 +16,22 @@ written by #ifndef INC_SRTC_H #define INC_SRTC_H +#ifndef SRT_API +#ifdef _WIN32 + #ifdef SRT_DYNAMIC + #ifdef SRT_EXPORTS + #define SRT_API __declspec(dllexport) + #else + #define SRT_API __declspec(dllimport) + #endif + #else // !SRT_DYNAMIC + #define SRT_API + #endif +#else + #define SRT_API __attribute__ ((visibility("default"))) +#endif +#endif + #include "version.h" #include "platform_sys.h" @@ -33,34 +49,6 @@ written by //if compiling with MinGW, it only works on XP or above //use -D_WIN32_WINNT=0x0501 - -#ifdef _WIN32 - #ifndef __MINGW32__ - // Explicitly define 32-bit and 64-bit numbers - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int32 uint32_t; - #ifndef LEGACY_WIN32 - typedef unsigned __int64 uint64_t; - #else - // VC 6.0 does not support unsigned __int64: may cause potential problems. - typedef __int64 uint64_t; - #endif - #endif - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else // !SRT_DYNAMIC - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif - - // For feature tests if you need. // You can use these constants with SRTO_MINVERSION option. #define SRT_VERSION_FEAT_HSv5 0x010300 diff --git a/srtcore/srt_attr_defs.h b/srtcore/srt_attr_defs.h index 84daabeb1..85ea9f96d 100644 --- a/srtcore/srt_attr_defs.h +++ b/srtcore/srt_attr_defs.h @@ -31,6 +31,14 @@ used by SRT library internally. #define ATR_DEPRECATED #endif +#if HAVE_CXX11 +#define SRT_ATR_ALIGNAS(n) alignas(n) +#elif HAVE_GCC +#define SRT_ATR_ALIGNAS(n) __attribute__((aligned(n))) +#else +#define SRT_ATR_ALIGNAS(n) +#endif + #if defined(__cplusplus) && __cplusplus > 199711L #define HAVE_CXX11 1 // For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on, diff --git a/srtcore/srt_compat.c b/srtcore/srt_compat.c index fbf4859ae..bbdf7f795 100644 --- a/srtcore/srt_compat.c +++ b/srtcore/srt_compat.c @@ -17,7 +17,6 @@ written by // Prevents from misconfiguration through preprocessor. #include "platform_sys.h" - #include #include diff --git a/srtcore/srt_compat.h b/srtcore/srt_compat.h index 960c1b85a..d4fd2361e 100644 --- a/srtcore/srt_compat.h +++ b/srtcore/srt_compat.h @@ -20,55 +20,12 @@ written by #include #include -#ifndef SRT_API -#ifdef _WIN32 - #ifndef __MINGW32__ - #ifdef SRT_DYNAMIC - #ifdef SRT_EXPORTS - #define SRT_API __declspec(dllexport) - #else - #define SRT_API __declspec(dllimport) - #endif - #else - #define SRT_API - #endif - #else - #define SRT_API - #endif -#else - #define SRT_API __attribute__ ((visibility("default"))) -#endif -#endif - -#ifdef _WIN32 - // https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "Id" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "Iu" - #endif -#else - // http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html - // printf() Format for ssize_t - #if !defined(PRIzd) - #define PRIzd "zd" - #endif - // printf() Format for size_t - #if !defined(PRIzu) - #define PRIzu "zu" - #endif -#endif - - #ifdef __cplusplus extern "C" { #endif /* Ensures that we store the error in the buffer and return the bufer. */ -SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen); +const char * SysStrError(int errnum, char * buf, size_t buflen); #ifdef __cplusplus } // extern C diff --git a/srtcore/utilities.h b/srtcore/utilities.h index 258a2fdc8..1786cf0ae 100644 --- a/srtcore/utilities.h +++ b/srtcore/utilities.h @@ -35,6 +35,7 @@ written by #include #include #include +#include #if HAVE_CXX11 #include @@ -236,17 +237,20 @@ written by #endif -// Hardware <--> Network (big endian) convention +/// Hardware --> Network (big-endian) byte order conversion +/// @param size source length in four octets inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = htonl(src[i]); + dst[i] = htobe32(src[i]); } +/// Network (big-endian) --> Hardware byte order conversion +/// @param size source length in four octets inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) - dst[i] = ntohl(src[i]); + dst[i] = be32toh(src[i]); } // Hardware <--> Intel (little endian) convention @@ -575,7 +579,7 @@ inline Stream& Print(Stream& in) { return in;} template inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) { - sout << arg1; + sout << std::forward(arg1); return Print(sout, args...); } @@ -682,7 +686,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 @@ -1280,6 +1284,7 @@ inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_ #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } +#define SRTU_PROPERTY_WO_ARG(type, name, expr) void set_##name(type arg) { expr; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) diff --git a/srtcore/window.h b/srtcore/window.h index 132440368..dbe4b7179 100644 --- a/srtcore/window.h +++ b/srtcore/window.h @@ -244,7 +244,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probeArrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE; + const int inorder16 = pkt.seqno() & PUMASK_SEQNO_PROBE; // for probe1, we want 16th packet if (inorder16 == 0) @@ -266,7 +266,7 @@ class CPktTimeWindow: CPktTimeWindowTools void probe1Arrival(const CPacket& pkt, bool unordered) { SRT_ASSERT(m_zHeaderSize != 0 && m_zPayloadSize != 0); - if (unordered && pkt.m_iSeqNo == m_Probe1Sequence) + if (unordered && pkt.seqno() == m_Probe1Sequence) { // Reset the starting probe into "undefined", when // a packet has come as retransmitted before the @@ -276,7 +276,7 @@ class CPktTimeWindow: CPktTimeWindowTools } m_tsProbeTime = sync::steady_clock::now(); - m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken + m_Probe1Sequence = pkt.seqno(); // Record the sequence where 16th packet probe was taken } /// Record the arrival time of the second probing packet and the interval between packet pairs. @@ -292,7 +292,7 @@ class CPktTimeWindow: CPktTimeWindowTools // expected packet pair, behave as if the 17th packet was lost. // no start point yet (or was reset) OR not very next packet - if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo) + if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.seqno()) return; // Grab the current time before trying to acquire diff --git a/test/test_bonding.cpp b/test/test_bonding.cpp index a03b7c5a0..241d26d99 100644 --- a/test/test_bonding.cpp +++ b/test/test_bonding.cpp @@ -330,3 +330,130 @@ TEST(Bonding, CloseGroupAndSocket) listen_promise.wait(); } +TEST(Bonding, Options) +{ + using namespace std; + using namespace srt; + + TestInit srtinit; + + // Create a group + const SRTSOCKET grp = srt_create_group(SRT_GTYPE_BROADCAST); + + // rendezvous shall not be allowed to be set on the group + // XXX actually it is possible, but no one tested it. POSTPONE. + //int yes = 1; + //EXPECT_EQ(srt_setsockflag(grp, SRTO_RENDEZVOUS, &yes, sizeof yes), SRT_ERROR); + +#ifdef SRT_ENABLE_ENCRYPTION + string pass = "longenoughpassword"; + // passphrase should be ok. + EXPECT_NE(srt_setsockflag(grp, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + + int lat = 500; + EXPECT_NE(srt_setsockflag(grp, SRTO_RCVLATENCY, &lat, sizeof lat), SRT_ERROR); + + mutex mx; + condition_variable latch; + atomic started {false}; + + thread accept_and_close { [&]() { + + unique_lock ux(mx); + + SRTSOCKET lsn = srt_create_socket(); +#ifdef SRT_ENABLE_ENCRYPTION + EXPECT_NE(srt_setsockflag(lsn, SRTO_PASSPHRASE, pass.c_str(), pass.size()), SRT_ERROR); +#endif + int allow = 1; + ASSERT_NE(srt_setsockflag(lsn, SRTO_GROUPCONNECT, &allow, sizeof allow), SRT_ERROR); + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + ASSERT_NE(srt_bind(lsn, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(lsn, 1), SRT_ERROR); + started = true; + + // First wait - until it's let go with accepting + latch.wait(ux); + + sockaddr_any revsa; + SRTSOCKET gs = srt_accept(lsn, revsa.get(), &revsa.len); + ASSERT_NE(gs, SRT_ERROR); + + // Connected, wait to close + latch.wait(ux); + + srt_close(gs); + srt_close(lsn); + }}; + + // Give the thread a chance to start + this_thread::yield(); + + while (!started) + { + // In case of a bad luck, just wait for the thread to + // acquire the mutex before you do + this_thread::sleep_for(chrono::milliseconds(10)); + } + + // Wait for the possibility to connect + { + // Make sure that the thread reached the wait() call. + unique_lock ux(mx); + latch.notify_all(); + } + + // Now the thread is accepting, so we call the connect. + sockaddr_any sa = CreateAddr("127.0.0.1", 5555, AF_INET); + SRTSOCKET member = srt_connect(grp, sa.get(), sa.size()); + + // We've released the mutex and signaled the CV, so accept should proceed now. + // Exit from srt_connect() means also exit from srt_accept(). + + EXPECT_NE(member, SRT_INVALID_SOCK); + + // conenct_res should be a socket + EXPECT_NE(member, 0); // XXX Change to SRT_SOCKID_CONNREQ + + // Now get the option value from the group + + int revlat = -1; + int optsize = sizeof revlat; + EXPECT_NE(srt_getsockflag(grp, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + revlat = -1; + optsize = sizeof revlat; + // Expect the same value set on the member socket + EXPECT_NE(srt_getsockflag(member, SRTO_RCVLATENCY, &revlat, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof revlat); + EXPECT_EQ(revlat, 500); + + // Individual socket option modified on group + int ohead = 12; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(grp, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // Modifyting a post-option should be possible on a socket + ohead = 11; + optsize = sizeof ohead; + EXPECT_NE(srt_setsockflag(member, SRTO_OHEADBW, &ohead, optsize), SRT_ERROR); + + // But getting the option value should be equal to the group setting + EXPECT_NE(srt_getsockflag(grp, SRTO_OHEADBW, &ohead, &optsize), SRT_ERROR); + EXPECT_EQ(optsize, sizeof ohead); + EXPECT_EQ(ohead, 12); + + // We're done, the thread can close connection and exit + { + // Make sure that the thread reached the wait() call. + std::unique_lock ux(mx); + latch.notify_all(); + } + + accept_and_close.join(); + srt_close(grp); +} + diff --git a/test/test_buffer_rcv.cpp b/test/test_buffer_rcv.cpp index 0c67fa91c..554c6aae5 100644 --- a/test/test_buffer_rcv.cpp +++ b/test/test_buffer_rcv.cpp @@ -54,22 +54,25 @@ class CRcvBufferReadMsg EXPECT_NE(unit, nullptr); CPacket& packet = unit->m_Packet; - packet.m_iSeqNo = seqno; - packet.m_iTimeStamp = ts; + packet.set_seqno(seqno); + packet.set_timestamp(ts); packet.setLength(m_payload_sz); - generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo); + generatePayload(packet.data(), packet.getLength(), packet.seqno()); - packet.m_iMsgNo = msgno; - packet.m_iMsgNo |= PacketBoundaryBits(PB_SUBSEQUENT); + int32_t pktMsgFlags = msgno; + pktMsgFlags |= PacketBoundaryBits(PB_SUBSEQUENT); if (pb_first) - packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST); + pktMsgFlags |= PacketBoundaryBits(PB_FIRST); if (pb_last) - packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST); + pktMsgFlags |= PacketBoundaryBits(PB_LAST); + + if (!out_of_order) + pktMsgFlags |= MSGNO_PACKET_INORDER::wrap(1); + packet.set_msgflags(pktMsgFlags); if (!out_of_order) { - packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1); EXPECT_TRUE(packet.getMsgOrderFlag()); } diff --git a/test/test_common.cpp b/test/test_common.cpp index eee51e5e0..8d566b359 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "gtest/gtest.h" @@ -80,8 +81,7 @@ TEST(CIPAddress, IPv4_in_IPv6_pton) TEST(SRTAPI, SyncRendezvousHangs) { - ASSERT_EQ(srt_startup(), 0); - + srt::TestInit srtinit; int yes = 1; SRTSOCKET m_bindsock = srt_create_socket(); @@ -115,11 +115,41 @@ TEST(SRTAPI, SyncRendezvousHangs) duration = std::chrono::duration_cast(end - start).count(); }); - ASSERT_EQ(srt_rendezvous(m_bindsock, (sockaddr*)&local_sa, sizeof local_sa, + EXPECT_EQ(srt_rendezvous(m_bindsock, (sockaddr*)&local_sa, sizeof local_sa, (sockaddr*)&peer_sa, sizeof peer_sa), SRT_ERROR); close_thread.join(); ASSERT_LE(duration, 1); - srt_close(m_bindsock); - srt_cleanup(); +} + +TEST(SRTAPI, RapidClose) +{ + ASSERT_EQ(srt_startup(), 0); + + SRTSOCKET sock = srt_create_socket(); + std::condition_variable cv_start; + std::mutex cvm; + bool started = false, ended = false; + + std::thread connect_thread([&sock, &cv_start, &started, &ended] { + started = true; + cv_start.notify_one(); + + // Nonexistent address + sockaddr_any sa = CreateAddr("localhost", 5555, AF_INET); + srt_connect(sock, sa.get(), sa.size()); + // It doesn't matter if it succeeds. Important is that it exits. + ended = true; + }); + + std::unique_lock lk(cvm); + + // Wait until the thread surely starts + while (!started) + cv_start.wait(lk); + + srt_close(sock); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + EXPECT_TRUE(ended); + connect_thread.join(); } diff --git a/test/test_connection_timeout.cpp b/test/test_connection_timeout.cpp index 0b8bb7874..dca7595b8 100644 --- a/test/test_connection_timeout.cpp +++ b/test/test_connection_timeout.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "test_env.h" @@ -12,6 +13,7 @@ typedef int SOCKET; #include"platform_sys.h" #include "srt.h" +#include "netinet_any.h" using namespace std; @@ -204,3 +206,68 @@ TEST_F(TestConnectionTimeout, BlockingLoop) } +TEST(TestConnectionAPI, Accept) +{ + using namespace std::chrono; + using namespace srt; + + srt_startup(); + + const SRTSOCKET caller_sock = srt_create_socket(); + const SRTSOCKET listener_sock = srt_create_socket(); + + const int eidl = srt_epoll_create(); + const int eidc = srt_epoll_create(); + const int ev_conn = SRT_EPOLL_OUT | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidc, caller_sock, &ev_conn); + const int ev_acp = SRT_EPOLL_IN | SRT_EPOLL_ERR; + srt_epoll_add_usock(eidl, listener_sock, &ev_acp); + + sockaddr_any sa = srt::CreateAddr("localhost", 5555, AF_INET); + + ASSERT_NE(srt_bind(listener_sock, sa.get(), sa.size()), -1); + ASSERT_NE(srt_listen(listener_sock, 1), -1); + + // Set non-blocking mode so that you can wait for readiness + bool no = false; + srt_setsockflag(caller_sock, SRTO_RCVSYN, &no, sizeof no); + srt_setsockflag(listener_sock, SRTO_RCVSYN, &no, sizeof no); + + srt_connect(caller_sock, sa.get(), sa.size()); + + SRT_EPOLL_EVENT ready[2]; + int nready = srt_epoll_uwait(eidl, ready, 2, 1000); // Wait 1s + EXPECT_EQ(nready, 1); + EXPECT_EQ(ready[0].fd, listener_sock); + // EXPECT_EQ(ready[0].events, SRT_EPOLL_IN); + + // Now call the accept function incorrectly + int size = 0; + sockaddr_storage saf; + + EXPECT_EQ(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + std::this_thread::sleep_for(seconds(1)); + + // Set correctly + size = sizeof (sockaddr_in6); + EXPECT_NE(srt_accept(listener_sock, (sockaddr*)&saf, &size), SRT_ERROR); + + // Ended up with error, but now you should also expect error on the caller side. + + // Wait 5s until you get a connection broken. + nready = srt_epoll_uwait(eidc, ready, 2, 5000); + EXPECT_EQ(nready, 1); + if (nready == 1) + { + // Do extra checks only if you know that this was returned. + EXPECT_EQ(ready[0].fd, caller_sock); + EXPECT_EQ(ready[0].events & SRT_EPOLL_ERR, 0); + } + srt_close(caller_sock); + srt_close(listener_sock); + + srt_cleanup(); +} + + diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp index ce68dd32e..f4fa7f614 100644 --- a/test/test_crypto.cpp +++ b/test/test_crypto.cpp @@ -85,9 +85,9 @@ namespace srt const int inorder = 1; const int kflg = m_crypt.getSndCryptoFlags(); - pkt.m_iSeqNo = seqno; - pkt.m_iMsgNo = msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg);; - pkt.m_iTimeStamp = 356; + pkt.set_seqno(seqno); + pkt.set_msgflags(msgno | inorder | PacketBoundaryBits(PB_SOLO) | MSGNO_ENCKEYSPEC::wrap(kflg)); + pkt.set_timestamp(356); std::iota(pkt.data(), pkt.data() + pld_size, '0'); pkt.setLength(pld_size); @@ -103,7 +103,6 @@ namespace srt // Modify the payload and expect auth to fail. pkt_enc->data()[10] = '5'; EXPECT_EQ(m_crypt.decrypt(*pkt_enc.get()), ENCS_FAILED); - } } // namespace srt diff --git a/test/test_enforced_encryption.cpp b/test/test_enforced_encryption.cpp index 6fd284c23..717b4549c 100644 --- a/test/test_enforced_encryption.cpp +++ b/test/test_enforced_encryption.cpp @@ -257,8 +257,16 @@ class TestEnforcedEncryption { // Code here will be called just after the test completes. // OK to throw exceptions from here if needed. - EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); - EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + + if (m_caller_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_caller_socket), SRT_ERROR) << srt_getlasterror_str(); + } + + if (m_listener_socket != SRT_INVALID_SOCK) + { + EXPECT_NE(srt_close(m_listener_socket), SRT_ERROR) << srt_getlasterror_str(); + } } @@ -541,6 +549,7 @@ class TestEnforcedEncryption // Just give it some time and close the socket. std::this_thread::sleep_for(std::chrono::milliseconds(50)); ASSERT_NE(srt_close(m_listener_socket), SRT_ERROR); + m_listener_socket = SRT_INVALID_SOCK; // mark closed already accepting_thread.join(); } } diff --git a/test/test_env.h b/test/test_env.h index 68ec516b3..e20905351 100644 --- a/test/test_env.h +++ b/test/test_env.h @@ -63,18 +63,26 @@ class TestInit class UniqueSocket { int32_t sock; + std::string lab, f; + int l; public: - UniqueSocket(int32_t s): sock(s) + UniqueSocket(int32_t s, const char* label, const char* file, int line): sock(s) { if (s == -1) throw std::invalid_argument("Invalid socket"); + lab = label; + f = file; + l = line; } - UniqueSocket(): sock(-1) +#define MAKE_UNIQUE_SOCK(name, label, expr) srt::UniqueSocket name (expr, label, __FILE__, __LINE__) + + UniqueSocket(): sock(-1), l(0) { } + void close(); ~UniqueSocket(); operator int32_t() const diff --git a/test/test_epoll.cpp b/test/test_epoll.cpp index 116978c08..a8a88aa7a 100644 --- a/test/test_epoll.cpp +++ b/test/test_epoll.cpp @@ -66,7 +66,7 @@ TEST(CEPoll, WaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -89,7 +89,7 @@ TEST(CEPoll, UWaitEmptyCall) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -112,7 +112,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -146,7 +146,7 @@ TEST(CEPoll, WaitAllSocketsInEpollReleased2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -174,7 +174,7 @@ TEST(CEPoll, WrongEpoll_idOnAddUSock) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int no = 0; @@ -197,7 +197,7 @@ TEST(CEPoll, HandleEpollEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -258,7 +258,7 @@ TEST(CEPoll, NotifyConnectionBreak) srt::TestInit srtinit; // 1. Prepare client - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); ASSERT_NE(client_sock, SRT_ERROR); const int yes SRT_ATR_UNUSED = 1; @@ -280,7 +280,7 @@ TEST(CEPoll, NotifyConnectionBreak) ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa_client.sin_addr), 1); // 2. Prepare server - srt::UniqueSocket server_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(server_sock, "server_sock", srt_create_socket()); ASSERT_NE(server_sock, SRT_ERROR); ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect @@ -372,7 +372,7 @@ TEST(CEPoll, HandleEpollEvent2) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -433,7 +433,7 @@ TEST(CEPoll, HandleEpollNoEvent) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int yes = 1; @@ -483,7 +483,7 @@ TEST(CEPoll, ThreadedUpdate) { srt::TestInit srtinit; - srt::UniqueSocket client_sock = srt_create_socket(); + MAKE_UNIQUE_SOCK(client_sock, "client", srt_create_socket()); EXPECT_NE(client_sock, SRT_ERROR); const int no = 0; diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index 602ff6d13..185b54e98 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -305,9 +305,12 @@ TEST(TestFEC, Connection) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); - SRTSOCKET la[] = { l }; + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + // Given 2s timeout for accepting as it has occasionally happened with Travis // that 1s might not be enough. + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); EXPECT_EQ(connect_res.get(), SRT_SUCCESS); @@ -362,6 +365,9 @@ TEST(TestFEC, ConnectionReorder) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -417,6 +423,9 @@ TEST(TestFEC, ConnectionFull1) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -472,6 +481,9 @@ TEST(TestFEC, ConnectionFull2) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -527,6 +539,9 @@ TEST(TestFEC, ConnectionMess) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -580,6 +595,9 @@ TEST(TestFEC, ConnectionForced) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + SRTSOCKET la[] = { l }; SRTSOCKET a = srt_accept_bond(la, 1, 2000); ASSERT_NE(a, SRT_ERROR); @@ -630,6 +648,9 @@ TEST(TestFEC, RejectionConflict) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -671,6 +692,9 @@ TEST(TestFEC, RejectionIncompleteEmpty) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -716,6 +740,9 @@ TEST(TestFEC, RejectionIncomplete) return srt_connect(s, (sockaddr*)& sa, sizeof(sa)); }); + // Make sure that the async call to srt_connect() is already kicked. + std::this_thread::yield(); + EXPECT_EQ(connect_res.get(), SRT_ERROR); EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER); @@ -809,7 +836,7 @@ TEST_F(TestFECRebuilding, NoRebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -886,7 +913,7 @@ TEST_F(TestFECRebuilding, Rebuild) // - Crypto // - Message Number // will be set to 0/false - fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO); + fecpkt->set_msgflags(MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO)); // ... and then fix only the Crypto flags fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0)); @@ -904,7 +931,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/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index 0df06916b..61d3d6a20 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -26,12 +26,14 @@ #include #include #include +#include //#pragma comment (lib, "ws2_32.lib") TEST(Transmission, FileUpload) { srt::TestInit srtinit; + srtinit.HandlePerTestOptions(); // Generate the source file // We need a file that will contain more data @@ -94,7 +96,7 @@ TEST(Transmission, FileUpload) // Start listener-receiver thread - bool thread_exit = false; + std::atomic thread_exit { false }; auto client = std::thread([&] { @@ -118,15 +120,15 @@ TEST(Transmission, FileUpload) for (;;) { int n = srt_recv(accepted_sock, buf.data(), 1456); - EXPECT_NE(n, SRT_ERROR); - if (n == -1) + EXPECT_NE(n, SRT_ERROR) << srt_getlasterror_str(); + if (n == 0) { - std::cerr << "UNEXPECTED ERROR: " << srt_getlasterror_str() << std::endl; + std::cerr << "Received 0 bytes, breaking.\n"; break; } - if (n == 0) + else if (n == -1) { - std::cerr << "Received 0 bytes, breaking.\n"; + std::cerr << "READ FAILED, breaking anyway\n"; break; } @@ -180,7 +182,7 @@ TEST(Transmission, FileUpload) std::cout << "Sockets closed, joining receiver thread\n"; client.join(); - std::ifstream tarfile("file.target"); + std::ifstream tarfile("file.target", std::ios::in | std::ios::binary); EXPECT_EQ(!!tarfile, true); tarfile.seekg(0, std::ios::end); @@ -189,8 +191,14 @@ TEST(Transmission, FileUpload) std::cout << "Comparing files\n"; // Compare files - tarfile.seekg(0, std::ios::end); - ifile.seekg(0, std::ios::beg); + + // Theoretically it should work if you just rewind to 0, but + // on Windows this somehow doesn't work. + tarfile.close(); + tarfile.open("file.target", std::ios::in | std::ios::binary); + + ifile.close(); + ifile.open("file.source", std::ios::in | std::ios::binary); for (size_t i = 0; i < tar_size; ++i) { diff --git a/test/test_main.cpp b/test/test_main.cpp index cc5acc487..e2243a306 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -113,6 +113,11 @@ void TestInit::HandlePerTestOptions() { srt_setloglevel(LOG_DEBUG); } + + if (TestEnv::me->OptionPresent("lognote")) + { + srt_setloglevel(LOG_NOTICE); + } } // Copied from ../apps/apputil.cpp, can't really link this file here. @@ -178,7 +183,33 @@ sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_f UniqueSocket::~UniqueSocket() { - srt_close(sock); + // Could be closed explicitly + if (sock != -1) + close(); +} + +void UniqueSocket::close() +{ + int close_result = srt_close(sock); + int close_error = srt_getlasterror(nullptr); + + // XXX SRT_EINVSOCK is reported when the socket + // has been already wiped out, which may happen to a broken socket. + // This isn't exactly intended, although trying to close a nonexistent + // socket is not a problem, as long as it happens before the id value rollover + // (that is, when it's closed immediately after getting broken). + // This solution is still slick though and should be fixed. + // + // Restore this, when fixed + // EXPECT_NE(srt_close(sock), SRT_ERROR) << lab << " CREATED: "<< f << ":" << l; + if (close_result == SRT_ERROR) + { + EXPECT_NE(close_error, SRT_EINVSOCK) << lab << " CREATED: "<< f << ":" << l; + } + else + { + EXPECT_EQ(close_result, 0) << lab << " CREATED: "<< f << ":" << l; + } } } diff --git a/test/test_many_connections.cpp b/test/test_many_connections.cpp index 20deccf70..29cd6bdd2 100644 --- a/test/test_many_connections.cpp +++ b/test/test_many_connections.cpp @@ -135,6 +135,8 @@ TEST_F(TestConnection, Multiple) cerr << "Opening " << NSOCK << " connections\n"; + bool overall_test = true; + for (size_t i = 0; i < NSOCK; i++) { m_connections[i] = srt_create_socket(); @@ -145,13 +147,18 @@ TEST_F(TestConnection, Multiple) int conntimeo = 60; srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo); + SRTSOCKET connres = SRT_INVALID_SOCK; + //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n"; //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl; - ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR); + connres = srt_connect(m_connections[i], psa, sizeof lsa); + EXPECT_NE(connres, SRT_INVALID_SOCK) << "conn #" << i << ": " << srt_getlasterror_str(); + if (connres == SRT_INVALID_SOCK) + overall_test = false; // Set now async sending so that sending isn't blocked int no = 0; - ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); + EXPECT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1); } for (size_t j = 1; j <= 100; j++) @@ -170,6 +177,7 @@ TEST_F(TestConnection, Multiple) EXPECT_FALSE(m_accept_exit) << "AcceptLoop already broken for some reason!"; // Up to this moment the server sock should survive + cerr << "Closing server socket\n"; // Close server socket to break the accept loop EXPECT_EQ(srt_close(m_server_sock), 0); @@ -177,6 +185,8 @@ TEST_F(TestConnection, Multiple) cerr << "Synchronize with the accepting thread\n"; ex.wait(); cerr << "Synchronization done\n"; + + ASSERT_TRUE(overall_test); } diff --git a/test/test_reuseaddr.cpp b/test/test_reuseaddr.cpp index fe9027311..d00628e63 100644 --- a/test/test_reuseaddr.cpp +++ b/test/test_reuseaddr.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifndef _WIN32 #include #endif @@ -30,7 +31,7 @@ struct AtReturnJoin // iphlp library to be attached to the executable, which is kinda // problematic. Temporarily block tests using this function on Windows. -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n"; return ""; @@ -50,7 +51,7 @@ struct IfAddr } }; -std::string GetLocalIP(int af = AF_UNSPEC) +static std::string GetLocalIP(int af = AF_UNSPEC) { struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; @@ -101,306 +102,360 @@ std::string GetLocalIP(int af = AF_UNSPEC) } #endif -int client_pollid = SRT_ERROR; -SRTSOCKET g_client_sock = SRT_ERROR; - -void clientSocket(std::string ip, int port, bool expect_success) +class ReuseAddr : public srt::Test { - int yes = 1; - int no = 0; +protected: - int family = AF_INET; - std::string famname = "IPv4"; - if (ip.substr(0, 2) == "6.") + std::string showEpollContents(const char* label, int* array, int length) { - family = AF_INET6; - ip = ip.substr(2); - famname = "IPv6"; - } - - std::cout << "[T/C] Creating client socket\n"; - - g_client_sock = srt_create_socket(); - ASSERT_NE(g_client_sock, SRT_ERROR); + std::ostringstream out; + out << label << ":["; + if (length) + { + // Now is at least 1 + out << "@" << array[0]; - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect - ASSERT_NE(srt_setsockflag(g_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + for (int i = 1; i < length; ++i) + out << " @" << array[i]; + } + out << "]"; + return out.str(); + } - ASSERT_NE(srt_setsockopt(g_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + struct UniquePollid + { + int pollid = SRT_ERROR; + UniquePollid() + { + pollid = srt_epoll_create(); + } - int epoll_out = SRT_EPOLL_OUT; - srt_epoll_add_usock(client_pollid, g_client_sock, &epoll_out); + ~UniquePollid() + { + srt_epoll_release(pollid); + } - sockaddr_any sa = srt::CreateAddr(ip, port, family); + operator int() const + { + return pollid; + } + }; - std::cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << std::endl; + void clientSocket(SRTSOCKET client_sock, std::string ip, int port, bool expect_success) + { + using namespace std; - int connect_res = srt_connect(g_client_sock, sa.get(), sa.size()); + int yes = 1; + int no = 0; - if (connect_res == -1) - { - std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl; - } + int family = AF_INET; + string famname = "IPv4"; + if (ip.substr(0, 2) == "6.") + { + family = AF_INET6; + ip = ip.substr(2); + famname = "IPv6"; + } - if (expect_success) - { - EXPECT_NE(connect_res, -1); - if (connect_res == -1) - return; + cout << "[T/C] Setting up client socket\n"; + ASSERT_NE(client_sock, SRT_INVALID_SOCK); + ASSERT_EQ(srt_getsockstate(client_sock), SRTS_INIT); - // Socket readiness for connection is checked by polling on WRITE allowed sockets. + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR); + EXPECT_NE(srt_setsockflag(client_sock, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); - if (connect_res != -1) - { - int rlen = 2; - SRTSOCKET read[2]; + UniquePollid client_pollid; + ASSERT_NE(int(client_pollid), SRT_ERROR); - int wlen = 2; - SRTSOCKET write[2]; + int epoll_out = SRT_EPOLL_OUT; + srt_epoll_add_usock(client_pollid, client_sock, &epoll_out); - std::cout << "[T/C] Waiting for connection readiness...\n"; + sockaddr_any sa = srt::CreateAddr(ip, port, family); - EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR); + cout << "[T/C] Connecting to: " << sa.str() << " (" << famname << ")" << endl; + int connect_res = srt_connect(client_sock, sa.get(), sa.size()); - EXPECT_EQ(rlen, 0); // get exactly one write event without reads - EXPECT_EQ(wlen, 1); // get exactly one write event without reads - EXPECT_EQ(write[0], g_client_sock); // for our client socket + if (connect_res == -1) + { + cout << "srt_connect: " << srt_getlasterror_str() << endl; + } - char buffer[1316] = {1, 2, 3, 4}; - EXPECT_NE(srt_sendmsg(g_client_sock, buffer, sizeof buffer, - -1, // infinit ttl - true // in order must be set to true - ), - SRT_ERROR); + if (expect_success) + { + EXPECT_NE(connect_res, -1); + if (connect_res == -1) + return; + + // Socket readiness for connection is checked by polling on WRITE allowed sockets. + + if (connect_res != -1) + { + int rlen = 2; + SRTSOCKET read[2]; + + int wlen = 2; + SRTSOCKET write[2]; + + cout << "[T/C] Waiting for connection readiness...\n"; + + EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); + + EXPECT_EQ(rlen, 0) << showEpollContents("[T/C] R", read, rlen); // get exactly one write event without reads + EXPECT_EQ(wlen, 1) << showEpollContents("[T/C] W", write, wlen); // get exactly one write event without reads + EXPECT_EQ(write[0], client_sock); // for our client socket + + char buffer[1316] = {1, 2, 3, 4}; + EXPECT_NE(srt_sendmsg(client_sock, buffer, sizeof buffer, + -1, // infinit ttl + true // in order must be set to true + ), + SRT_ERROR); + } + else + { + cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + } } else { - std::cout << "[T/C] (NOT TESTING TRANSMISSION - CONNECTION FAILED ALREADY)\n"; + EXPECT_EQ(connect_res, -1); } + + cout << "[T/C] Client exit\n"; } - else + + SRTSOCKET prepareServerSocket() { - EXPECT_EQ(connect_res, -1); - } + SRTSOCKET bindsock = srt_create_socket(); + EXPECT_NE(bindsock, SRT_ERROR); - std::cout << "[T/C] Client exit\n"; -} + int yes = 1; + int no = 0; -int server_pollid = SRT_ERROR; + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); -SRTSOCKET prepareSocket() -{ - SRTSOCKET bindsock = srt_create_socket(); - EXPECT_NE(bindsock, SRT_ERROR); + return bindsock; + } - int yes = 1; - int no = 0; + bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) + { + sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR); + std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; - int epoll_in = SRT_EPOLL_IN; + std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; - std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; - srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); + int bind_res = srt_bind(bindsock, sa.get(), sa.size()); - return bindsock; -} + std::cout << "[T/S] ... result " << bind_res << " (expected to " + << (expect_success ? "succeed" : "fail") << ")\n"; -bool bindSocket(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - sockaddr_any sa = srt::CreateAddr(ip, port, AF_INET); + if (!expect_success) + { + std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; + EXPECT_EQ(bind_res, SRT_ERROR); + return false; + } - std::string fam = (sa.family() == AF_INET) ? "IPv4" : "IPv6"; + EXPECT_NE(bind_res, SRT_ERROR); + return true; + } - std::cout << "[T/S] Bind @" << bindsock << " to: " << sa.str() << " (" << fam << ")" << std::endl; + bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) + { + if (!bindSocket(bindsock, ip, port, expect_success)) + return false; - int bind_res = srt_bind(bindsock, sa.get(), sa.size()); + EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); - std::cout << "[T/S] ... result " << bind_res << " (expected to " - << (expect_success ? "succeed" : "fail") << ")\n"; + return true; + } - if (!expect_success) + SRTSOCKET createListener(std::string ip, int port, bool expect_success) { - std::cout << "[T/S] Binding should fail: " << srt_getlasterror_str() << std::endl; - EXPECT_EQ(bind_res, SRT_ERROR); - return false; - } + std::cout << "[T/S] serverSocket: creating listener socket\n"; - EXPECT_NE(bind_res, SRT_ERROR); - return true; -} + SRTSOCKET bindsock = prepareServerSocket(); -bool bindListener(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ - if (!bindSocket(bindsock, ip, port, expect_success)) - return false; + if (!bindListener(bindsock, ip, port, expect_success)) + return SRT_INVALID_SOCK; - EXPECT_NE(srt_listen(bindsock, SOMAXCONN), SRT_ERROR); + return bindsock; + } - return true; -} + SRTSOCKET createBinder(std::string ip, int port, bool expect_success) + { + std::cout << "[T/S] serverSocket: creating binder socket\n"; -SRTSOCKET createListener(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating listener socket\n"; + SRTSOCKET bindsock = prepareServerSocket(); - SRTSOCKET bindsock = prepareSocket(); + if (!bindSocket(bindsock, ip, port, expect_success)) + { + srt_close(bindsock); + return SRT_INVALID_SOCK; + } - if (!bindListener(bindsock, ip, port, expect_success)) - return SRT_INVALID_SOCK; + return bindsock; + } - return bindsock; -} + void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) + { + MAKE_UNIQUE_SOCK(client_sock, "[T/S]connect", srt_create_socket()); -SRTSOCKET createBinder(std::string ip, int port, bool expect_success) -{ - std::cout << "[T/S] serverSocket: creating binder socket\n"; + auto run = [this, &client_sock, ip, port, expect_success]() { clientSocket(client_sock, ip, port, expect_success); }; - SRTSOCKET bindsock = prepareSocket(); + auto launched = std::async(std::launch::async, run); - if (!bindSocket(bindsock, ip, port, expect_success)) - { - srt_close(bindsock); - return SRT_INVALID_SOCK; - } + AtReturnJoin atreturn_join {launched}; - return bindsock; -} + int server_pollid = srt_epoll_create(); + int epoll_in = SRT_EPOLL_IN; + std::cout << "[T/S] Listener/binder sock @" << bindsock << " added to server_pollid\n"; + srt_epoll_add_usock(server_pollid, bindsock, &epoll_in); -void testAccept(SRTSOCKET bindsock, std::string ip, int port, bool expect_success) -{ + { // wait for connection from client + int rlen = 2; + SRTSOCKET read[2]; - auto run = [ip, port, expect_success]() { clientSocket(ip, port, expect_success); }; + int wlen = 2; + SRTSOCKET write[2]; - auto launched = std::async(std::launch::async, run); + std::cout << "[T/S] Wait 10s on E" << server_pollid << " for acceptance on @" << bindsock << " ...\n"; - AtReturnJoin atreturn_join {launched}; + EXPECT_NE(srt_epoll_wait(server_pollid, + read, &rlen, + write, &wlen, + 10000, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - { // wait for connection from client - int rlen = 2; - SRTSOCKET read[2]; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + ASSERT_EQ(read[0], bindsock); // read event is for bind socket + } - std::cout << "[T/S] Wait 10s for acceptance on @" << bindsock << " ...\n"; + { + sockaddr_any scl; + MAKE_UNIQUE_SOCK(accepted_sock, "[T/S]accept", srt_accept(bindsock, scl.get(), &scl.len)); - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - 10000, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + if (accepted_sock == -1) + { + std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; + } + EXPECT_NE(accepted_sock.ref(), SRT_INVALID_SOCK); + sockaddr_any showacp = (sockaddr*)&scl; + std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], bindsock); // read event is for bind socket - } + int epoll_in = SRT_EPOLL_IN; + srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input - sockaddr_any scl; + char buffer[1316]; + { // wait for 1316 packet from client + int rlen = 2; + SRTSOCKET read[2]; - SRTSOCKET accepted_sock = srt_accept(bindsock, scl.get(), &scl.len); - if (accepted_sock == -1) - { - std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl; - } - ASSERT_NE(accepted_sock, SRT_INVALID_SOCK); + int wlen = 2; + SRTSOCKET write[2]; - sockaddr_any showacp = (sockaddr*)&scl; - std::cout << "[T/S] Accepted from: " << showacp.str() << std::endl; + std::cout << "[T/S] Wait for data reception...\n"; - int epoll_in = SRT_EPOLL_IN; - srt_epoll_add_usock(server_pollid, accepted_sock, &epoll_in); // wait for input + EXPECT_NE(srt_epoll_wait(server_pollid, + read, &rlen, + write, &wlen, + -1, // -1 is set for debuging purpose. + // in case of production we need to set appropriate value + 0, 0, 0, 0), SRT_ERROR) << srt_getlasterror_str(); - char buffer[1316]; - { // wait for 1316 packet from client - int rlen = 2; - SRTSOCKET read[2]; - int wlen = 2; - SRTSOCKET write[2]; + EXPECT_EQ(rlen, 1) << showEpollContents("[T/S] R", read, rlen); // get exactly one read event without writes + EXPECT_EQ(wlen, 0) << showEpollContents("[T/S] W", write, wlen); // get exactly one read event without writes + EXPECT_EQ(read[0], accepted_sock.ref()); // read event is for bind socket + } - std::cout << "[T/S] Wait for data reception...\n"; + char pattern[4] = {1, 2, 3, 4}; - ASSERT_NE(srt_epoll_wait(server_pollid, - read, &rlen, - write, &wlen, - -1, // -1 is set for debuging purpose. - // in case of production we need to set appropriate value - 0, 0, 0, 0), SRT_ERROR ); + EXPECT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), + 1316); + EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); - ASSERT_EQ(rlen, 1); // get exactly one read event without writes - ASSERT_EQ(wlen, 0); // get exactly one read event without writes - ASSERT_EQ(read[0], accepted_sock); // read event is for bind socket - } + // XXX There is a possibility that a broken socket can be closed automatically, + // just the srt_close() call would simply return error in case of nonexistent + // socket. Therefore close them both at once; this problem needs to be fixed + // separately. + // + // The test only intends to send one portion of data from the client, so once + // received, the client has nothing more to do and should exit. + std::cout << "[T/S] closing client socket\n"; + client_sock.close(); + std::cout << "[T/S] closing sockets: ACP:@" << accepted_sock << "...\n"; + } + srt_epoll_release(server_pollid); - char pattern[4] = {1, 2, 3, 4}; + // client_sock closed through UniqueSocket. + // cannot close client_sock after srt_sendmsg because of issue in api.c:2346 - ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), - 1316); + std::cout << "[T/S] joining client async \n"; + launched.get(); + } - EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0); + static void shutdownListener(SRTSOCKET bindsock) + { + // Silently ignore. Usually it should have been checked earlier, + // and an invalid sock might be expected in particular tests. + if (bindsock == SRT_INVALID_SOCK) + return; - std::cout << "[T/S] 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 + int yes = 1; + EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect + EXPECT_NE(srt_close(bindsock), SRT_ERROR); - std::cout << "[T/S] joining client async...\n"; - launched.get(); -} + std::chrono::milliseconds check_period (100); + int credit = 400; // 10 seconds + auto then = std::chrono::steady_clock::now(); -void shutdownListener(SRTSOCKET bindsock) -{ - // Silently ignore. Usually it should have been checked earlier, - // and an invalid sock might be expected in particular tests. - if (bindsock == SRT_INVALID_SOCK) - return; + std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; + while (srt_getsockstate(bindsock) != SRTS_NONEXIST) + { + std::this_thread::sleep_for(check_period); + --credit; + if (!credit) + break; + } + auto now = std::chrono::steady_clock::now(); + auto dur = std::chrono::duration_cast(now - then); - int yes = 1; - EXPECT_NE(srt_setsockopt(bindsock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_ERROR); // for async connect - EXPECT_NE(srt_close(bindsock), SRT_ERROR); + // Keep as single string because this tends to be mixed from 2 threads. + std::ostringstream sout; + sout << "[T/S] @" << bindsock << " dissolved after " + << (dur.count() / 1000.0) << "s" << std::endl; + std::cout << sout.str() << std::flush; + + EXPECT_NE(credit, 0); + } - std::chrono::milliseconds check_period (100); - int credit = 400; // 10 seconds - auto then = std::chrono::steady_clock::now(); +private: - std::cout << "[T/S] waiting for cleanup of @" << bindsock << " up to 10s" << std::endl; - while (srt_getsockstate(bindsock) != SRTS_NONEXIST) + void setup() { - std::this_thread::sleep_for(check_period); - --credit; - if (!credit) - break; } - auto now = std::chrono::steady_clock::now(); - auto dur = std::chrono::duration_cast(now - then); - // Keep as single string because this tends to be mixed from 2 threads. - std::ostringstream sout; - sout << "[T/S] @" << bindsock << " dissolved after " - << (dur.count() / 1000.0) << "s" << std::endl; - std::cout << sout.str() << std::flush; - - EXPECT_NE(credit, 0); -} + void teardown() + { + } +}; -TEST(ReuseAddr, SameAddr1) +TEST_F(ReuseAddr, SameAddr1) { - srt::TestInit srtinit; - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener("127.0.0.1", 5000, true); @@ -413,24 +468,14 @@ TEST(ReuseAddr, SameAddr1) s1.join(); s2.join(); - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddr2) +TEST_F(ReuseAddr, SameAddr2) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder(localip, 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -445,21 +490,11 @@ TEST(ReuseAddr, SameAddr2) testAccept(bindsock_3, localip, 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, SameAddrV6) +TEST_F(ReuseAddr, SameAddrV6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); SRTSOCKET bindsock_1 = createBinder("::1", 5000, true); SRTSOCKET bindsock_2 = createListener("::1", 5000, true); @@ -475,26 +510,15 @@ TEST(ReuseAddr, SameAddrV6) testAccept(bindsock_3, "::1", 5000, true); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, DiffAddr) +TEST_F(ReuseAddr, DiffAddr) { - srt::TestInit srtinit; std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createBinder("127.0.0.1", 5000, true); SRTSOCKET bindsock_2 = createListener(localip, 5000, true); @@ -505,14 +529,10 @@ TEST(ReuseAddr, DiffAddr) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard) +TEST_F(ReuseAddr, Wildcard) { - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -524,14 +544,6 @@ TEST(ReuseAddr, Wildcard) std::string localip = GetLocalIP(AF_INET); if (localip == "") return; // DISABLE TEST if this doesn't work. - - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // Binding a certain address when wildcard is already bound should fail. @@ -541,15 +553,11 @@ TEST(ReuseAddr, Wildcard) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, Wildcard6) +TEST_F(ReuseAddr, Wildcard6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; @@ -567,17 +575,10 @@ TEST(ReuseAddr, Wildcard6) // performed there. std::string localip_v4 = GetLocalIP(AF_INET); - - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - // This must be obligatory set before binding a socket to "::" int strict_ipv6 = 1; - SRTSOCKET bindsock_1 = prepareSocket(); + SRTSOCKET bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -601,7 +602,7 @@ TEST(ReuseAddr, Wildcard6) strict_ipv6 = 0; - bindsock_1 = prepareSocket(); + bindsock_1 = prepareServerSocket(); srt_setsockflag(bindsock_1, SRTO_IPV6ONLY, &strict_ipv6, sizeof strict_ipv6); bindListener(bindsock_1, "::", 5000, true); @@ -620,33 +621,23 @@ TEST(ReuseAddr, Wildcard6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); shutdownListener(bindsock_3); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersion6) +TEST_F(ReuseAddr, ProtocolVersion6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int yes = 1; @@ -659,32 +650,23 @@ TEST(ReuseAddr, ProtocolVersion6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } -TEST(ReuseAddr, ProtocolVersionFaux6) +TEST_F(ReuseAddr, ProtocolVersionFaux6) { SRTST_REQUIRES(IPv6); - srt::TestInit srtinit; + #if defined(_WIN32) || defined(CYGWIN) std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n" "Forcing test to pass, PLEASE FIX.\n"; return; #endif - client_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, client_pollid); - - server_pollid = srt_epoll_create(); - ASSERT_NE(SRT_ERROR, server_pollid); - SRTSOCKET bindsock_1 = createListener("0.0.0.0", 5000, true); // We need a small interception in this one. - // createListener = prepareSocket | bindListener - SRTSOCKET bindsock_2 = prepareSocket(); + // createListener = prepareServerSocket | bindListener + SRTSOCKET bindsock_2 = prepareServerSocket(); { int no = 0; @@ -696,7 +678,4 @@ TEST(ReuseAddr, ProtocolVersionFaux6) shutdownListener(bindsock_1); shutdownListener(bindsock_2); - - (void)srt_epoll_release(client_pollid); - (void)srt_epoll_release(server_pollid); } diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index b7acda37a..271d84186 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -70,6 +70,9 @@ class TestSocketOptions }; auto accept_res = async(launch::async, accept_async, m_listen_sock); + // Make sure the thread was kicked + this_thread::yield(); + const int connect_res = Connect(); EXPECT_EQ(connect_res, SRT_SUCCESS); @@ -188,9 +191,12 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_LATENCY, "SRTO_LATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 200, {-1} }, //SRTO_LINGER { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} }, - //SRTO_MAXBW + { SRTO_MAXBW, "SRTO_MAXBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, +#ifdef ENABLE_MAXREXMITBW + { SRTO_MAXREXMITBW, "SRTO_MAXREXMITBW", RestrictionType::POST, sizeof(int64_t), int64_t(-1), INT64_MAX, int64_t(-1), int64_t(200000), {int64_t(-2)}}, +#endif { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, - //SRTO_MININPUTBW + { SRTO_MININPUTBW, "SRTO_MININPUTBW", RestrictionType::POST, sizeof(int64_t), int64_t(0), INT64_MAX, int64_t(0), int64_t(200000), {int64_t(-1)}}, { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, @@ -234,7 +240,7 @@ template void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc) { ValueType opt_val; - int opt_len = 0; + int opt_len = (int) entry.opt_len; EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS) << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str(); @@ -850,6 +856,29 @@ TEST_F(TestSocketOptions, StreamIDWrongLen) EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); } +//Check if setting -1 as optlen returns an error +TEST_F(TestSocketOptions, StringOptLenInvalid) +{ + const string test_string = "test1234567"; + const string srto_congestion_string ="live"; + const string fec_config = "fec,cols:10,rows:10"; + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_BINDTODEVICE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_CONGESTION, srto_congestion_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PACKETFILTER, fec_config.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); + + EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_PASSPHRASE, test_string.c_str(), -1), SRT_ERROR); + EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM); +} + // Try to set/get a 13-character string in SRTO_STREAMID. // This tests checks that the StreamID is set to the correct size // while it is transmitted as 16 characters in the Stream ID HS extension. diff --git a/test/test_sync.cpp b/test/test_sync.cpp index 844705ea6..e0454a581 100644 --- a/test/test_sync.cpp +++ b/test/test_sync.cpp @@ -587,7 +587,8 @@ TEST(SyncEvent, WaitForNotifyAll) /*****************************************************************************/ void* dummythread(void* param) { - *(bool*)(param) = true; + auto& thread_finished = *(srt::sync::atomic*)param; + thread_finished = true; return nullptr; } diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index c4c752667..17f4020dc 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -64,6 +64,7 @@ #include #include +#include "srt_compat.h" #include "apputil.hpp" #include "uriparser.hpp" // UriParser #include "socketoptions.hpp" diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index 03066363a..bf9739675 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -42,6 +42,10 @@ srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-mpbond"); +using namespace srt; +using namespace std; + + volatile bool mpbond_int_state = false; void OnINT_SetIntState(int) { @@ -49,9 +53,6 @@ void OnINT_SetIntState(int) mpbond_int_state = true; } -using namespace srt; - - int main( int argc, char** argv ) { // This is mainly required on Windows to initialize the network system, diff --git a/testing/srt-test-relay.cpp b/testing/srt-test-relay.cpp index dfabef62b..45d657128 100755 --- a/testing/srt-test-relay.cpp +++ b/testing/srt-test-relay.cpp @@ -45,6 +45,7 @@ written by #include "threadname.h" +using namespace std; bool Upload(UriParser& srt, UriParser& file); diff --git a/testing/testactivemedia.cpp b/testing/testactivemedia.cpp index 7d062f49d..96344f0b2 100644 --- a/testing/testactivemedia.cpp +++ b/testing/testactivemedia.cpp @@ -1,6 +1,9 @@ #include "testactivemedia.hpp" +using namespace std; + + void SourceMedium::Runner() { srt::ThreadName::set("SourceRN"); diff --git a/testing/testactivemedia.hpp b/testing/testactivemedia.hpp index f4bc360ba..011dcbfe7 100644 --- a/testing/testactivemedia.hpp +++ b/testing/testactivemedia.hpp @@ -59,7 +59,7 @@ struct Medium std::ostringstream tns; tns << typeid(*this).name() << ":" << this; srt::ThreadName tn(tns.str()); - thr = thread( [this] { RunnerBase(); } ); + thr = std::thread( [this] { RunnerBase(); } ); } void quit() @@ -89,12 +89,12 @@ struct Medium if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what(); else - cerr << "Transmission Error: " << e.what() << endl; + std::cerr << "Transmission Error: " << e.what() << std::endl; } catch (...) { if (Verbose::on) Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:"; else - cerr << "UNKNOWN EXCEPTION on medium\n"; + std::cerr << "UNKNOWN EXCEPTION on medium\n"; } } @@ -150,7 +150,7 @@ struct TargetMedium: Medium bool Schedule(const MediaPacket& data) { LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... "); - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state); if (!running || ::transmit_int_state) { @@ -166,13 +166,13 @@ struct TargetMedium: Medium void Clear() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); buffer.clear(); } void Interrupt() { - lock_guard lg(buffer_lock); + std::lock_guard lg(buffer_lock); running = false; ready.notify_one(); } diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index 337f5f365..be72471d1 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -29,8 +29,6 @@ extern std::atomic transmit_int_state; extern std::shared_ptr transmit_stats_writer; -using namespace std; - const srt_logging::LogFA SRT_LOGFA_APP = 10; extern srt_logging::Logger applog; @@ -57,7 +55,7 @@ class SrtCommon struct ConnectionBase { - string host; + std::string host; int port; int weight = 0; SRTSOCKET socket = SRT_INVALID_SOCK; @@ -65,7 +63,7 @@ class SrtCommon srt::sockaddr_any target; int token = -1; - ConnectionBase(string h, int p): host(h), port(p), source(AF_INET) {} + ConnectionBase(std::string h, int p): host(h), port(p), source(AF_INET) {} }; struct Connection: ConnectionBase @@ -76,7 +74,7 @@ class SrtCommon int error = SRT_SUCCESS; int reason = SRT_REJ_UNKNOWN; - Connection(string h, int p): ConnectionBase(h, p) {} + Connection(std::string h, int p): ConnectionBase(h, p) {} Connection(Connection&& old): ConnectionBase(old) { #if ENABLE_BONDING @@ -101,14 +99,14 @@ class SrtCommon int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_direction bool m_tsbpdmode = true; int m_outgoing_port = 0; - string m_mode; - string m_adapter; - map m_options; // All other options, as provided in the URI - vector m_group_nodes; - string m_group_type; - string m_group_config; + std::string m_mode; + std::string m_adapter; + std::map m_options; // All other options, as provided in the URI + std::vector m_group_nodes; + std::string m_group_type; + std::string m_group_config; #if ENABLE_BONDING - vector m_group_data; + std::vector m_group_data; #ifdef SRT_OLD_APP_READER int32_t m_group_seqno = -1; @@ -117,7 +115,7 @@ class SrtCommon int32_t sequence; bytevector packet; }; - map m_group_positions; + std::map m_group_positions; SRTSOCKET m_group_active; // The link from which the last packet was delivered #endif #endif @@ -131,8 +129,8 @@ class SrtCommon void UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size); public: - void InitParameters(string host, string path, map par); - void PrepareListener(string host, int port, int backlog); + void InitParameters(std::string host, std::string path, std::map par); + void PrepareListener(std::string host, int port, int backlog); void StealFrom(SrtCommon& src); void AcceptNewClient(); @@ -150,22 +148,22 @@ class SrtCommon protected: - void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); - void Init(string host, int port, string path, map par, SRT_EPOLL_OPT dir); + void Error(std::string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0); + void Init(std::string host, int port, std::string path, std::map par, SRT_EPOLL_OPT dir); int AddPoller(SRTSOCKET socket, int modes); virtual int ConfigurePost(SRTSOCKET sock); virtual int ConfigurePre(SRTSOCKET sock); - void OpenClient(string host, int port); + void OpenClient(std::string host, int port); #if ENABLE_BONDING void OpenGroupClient(); #endif void PrepareClient(); void SetupAdapter(const std::string& host, int port); - void ConnectClient(string host, int port); - void SetupRendezvous(string adapter, string host, int port); + void ConnectClient(std::string host, int port); + void SetupRendezvous(std::string adapter, std::string host, int port); - void OpenServer(string host, int port, int backlog = 1) + void OpenServer(std::string host, int port, int backlog = 1) { PrepareListener(host, port, backlog); if (transmit_accept_hook_fn) @@ -175,7 +173,7 @@ class SrtCommon AcceptNewClient(); } - void OpenRendezvous(string adapter, string host, int port) + void OpenRendezvous(std::string adapter, std::string host, int port) { PrepareClient(); SetupRendezvous(adapter, host, port); @@ -284,11 +282,11 @@ class SrtModel: public SrtCommon public: bool is_caller = false; bool is_rend = false; - string m_host; + std::string m_host; int m_port = 0; - SrtModel(string host, int port, map par); + SrtModel(std::string host, int port, std::map par); void Establish(std::string& w_name); void Close() @@ -320,7 +318,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon bool eof = true; public: - UdpSource(string host, int port, const map& attr); + UdpSource(std::string host, int port, const std::map& attr); MediaPacket Read(size_t chunk) override; @@ -331,7 +329,7 @@ class UdpSource: public virtual Source, public virtual UdpCommon class UdpTarget: public virtual Target, public virtual UdpCommon { public: - UdpTarget(string host, int port, const map& attr); + UdpTarget(std::string host, int port, const std::map& attr); void Write(const MediaPacket& data) override; bool IsOpen() override { return m_sock != -1; } @@ -341,7 +339,7 @@ class UdpTarget: public virtual Target, public virtual UdpCommon class UdpRelay: public Relay, public UdpSource, public UdpTarget { public: - UdpRelay(string host, int port, const map& attr): + UdpRelay(std::string host, int port, const std::map& attr): UdpSource(host, port, attr), UdpTarget(host, port, attr) {