Repository navigation
Conversation
A socket may now exchange datagrams through an owner-supplied transport instead of an OS UDP socket, without changing the behavior of regular UDP sockets in the same process. The seam lives behind CChannel: an attached socket gets a virtual channel arm whose sendto() invokes the owner's send callback - fire and forget, like a sendto() on a UDP socket, the transport owns delivery - and whose receiving worker picks up datagrams injected through srt_virtual_transport_inject(). Every attached socket owns a dedicated multiplexer and no OS socket is created for it; the send path performs no per-datagram allocation. The receiving worker is woken through a condition variable with a persistent woken flag (level-kept semantics, so a wake arriving while the worker is between waits is never lost) instead of an eventfd, so the seam is free of OS descriptor primitives and works on any platform; the 10 ms wait budget matches the poll()/select() timeouts of the UDP path. Heavy logging (ENABLE_DEBUG) compiles clean. The seam is compiled in only with the new ENABLE_VIRTUAL_TRANSPORT CMake option (default OFF; an OFF build is stock and unchanged). The option is supported on Linux and Emscripten - the Emscripten build defines the LINUX preprocessor macro only and includes musl's endian.h through utilities.h - and it requires at least C++11: a lower USE_CXX_STD together with the option is rejected at configure time. The new sources and tests carry the contributor copyright header. Verified: ENABLE_VIRTUAL_TRANSPORT=ON build with unit tests enabled passes the full TestVirtualTransport suite; the stock gate-OFF build still compiles.
…rtual transport New SRTO_SKIP_INDUCTION socket option (ENABLE_VIRTUAL_TRANSPORT builds only, pre-restriction, rejected together with SRTO_RENDEZVOUS). A caller with this option sends the HSv5 Conclusion immediately with cookie 0, skipping the Induction round trip; a listener with it accepts a Conclusion-first handshake. The cookie bypass is restricted to requests arriving over the listener's virtual transport channel, where peer authentication comes from the enclosing transport: a plain UDP listener keeps the stock cookie validation and treats a Conclusion-first request as one with a wrong cookie. For the same reason a caller that has this option set while exchanging datagrams over a plain UDP socket is rejected at connect time - it would send cookie-0 Conclusions that no stock listener answers, hanging until the connect timeout. A socket with an attached virtual transport may enter the listening state without a prior srt_bind() call: listen() synthesizes the virtual binding under the VIRTUAL_TRANSPORT_BIND_LABEL_ADDR label (10.213.255.254, an RFC 1918 address deliberately picked as an unlikely label of a real peer), which is purely reported through srt_getsockname() and never used for dispatching, since every attached socket owns a dedicated multiplexer. startConnect() registers the connector ahead of every later stage; the refusal paths that fire while the registration is live but before the socket reaches the connecting state (the SRTO_SKIP_INDUCTION rendezvous refusal, the plain-UDP caller gate, and the createCrypter()/ createSrtHandshake() rejections in the skip-induction caller setup) now remove the registration before throwing - an entry abandoned by such a throw would stay registered until its TTL expired (for rendezvous, the connect timeout times ten) and deadlock the teardown when the socket was closed inside that window. Verified: gate-ON build passes the full TestVirtualTransport suite (rendezvous refused in both option-setting orders, plain-UDP caller refused at connect, bind-less listen with concurrent sessions); the stock gate-OFF build still compiles.
New SRTO_INITIAL_RTT integer socket option (ENABLE_VIRTUAL_TRANSPORT builds only, pre-restriction, default 0 disabled, nonzero 1..30000 ms) that seeds the RTT estimator with an application-supplied RTT before the first live ACK/ACKACK sample: m_iSRTT is set to the value in microseconds and m_iRTTVar to mean/4 (the RFC 6298 variance/mean ratio). This exists because the skip-induction handshake removes the Induction round trip that normally delivers the first RTT sample; the enclosing transport (such as WebTransport/QUIC) knows the smoothed RTT and hands it to SRT so that cold-start retransmission timing (RTO and NAK intervals) starts from a realistic value. The seed is applied both in setOpt() and in open() - the latter because open() also runs at bind/connect/listen entry, after the option may have been set, and would otherwise reset the estimator to defaults. An accepted socket inherits the option from the listener through the config copy and is seeded by its own open() call. The seed survives connection completion on both roles: the per-peer-address CInfoBlock cache lookups in postConnect() and acceptAndRespond() are suppressed when a seed is explicitly set, because virtual transport reuses peer address labels across sessions and a cache hit could stomp the seed with a value of an unrelated session. Both lookup sites compute a single local use_rtt_cache flag (a constant true in stock builds) instead of duplicated ifdef branch bodies. The upstream first-live-sample behavior is untouched: m_bIsFirstRTTReceived stays false, so the first measured RTT replaces the seed. With the option unset, or in stock builds, the behavior is unchanged. Verified: gate-ON build passes the full TestVirtualTransport suite (including InitialRTTSeedThenLiveReplace); the stock gate-OFF build still compiles.
test_virtual_transport.cpp drives the seam over in-memory wires between attached sockets (owner send callback on one side, datagram injection on the other), with no OS socket involved. The suite covers: - end-to-end connectivity, with and without SRTO_SKIP_INDUCTION (Conclusion-first handshakes both directions); - the documented rejections: plain-UDP skip-induction caller refused at connect, SRTO_SKIP_INDUCTION refused together with SRTO_RENDEZVOUS in both option-setting orders, stock-socket listen without bind rejected, attach rejected on a bound or already-attached socket, detach rejected without a transport and inject refused after detach; - SRTO_INITIAL_RTT seeding the estimator and the first live sample replacing the seed, plus option readback; - receiver-side too-late drop of a missing packet; - bind-less listen: single session and several concurrent attached listeners that enter the listening state with no srt_bind() anywhere, each wired to its own caller through an independent transport, with the synthesized binding label reported by srt_getsockname() and the whole lifecycle creating no OS socket fd; - hardening: send-callback backpressure with queue overflow dropping injects, malformed injects rejected by size, detach during live traffic, and multiple accepts sharing one listener multiplexer. The suite runs inside the standard test-srt gtest binary and is exercised by the virtual transport CI workflow together with the full suite.
ENABLE_VIRTUAL_TRANSPORT existed only on the CMake surface: the legacy Tcl configure wrapper lists its explicit options from the cmake_options set in configure-data.tcl, which did not carry the new option (it only worked as an unlisted blind pass-through, invisible in --help and without --disable support). The option was therefore undiscoverable from the secondary build surface. Added enable-virtual-transport to cmake_options. The entry follows the repo's own regeneration process (scripts/generate-configure-options.tcl run over a CMakeCache.txt): the option name and its position in the list match the generated output, and the description is the full text of the option() call in CMakeLists.txt, as every checked-in entry keeps the untruncated description. This is all the legacy flow needs: the wrapper translates --enable-virtual-transport into -DENABLE_VIRTUAL_TRANSPORT=1 for cmake, which then enforces the platform and C++11 minimum gates; the CMake-side gating stays the single authority, so no checks were duplicated into the Tcl layer. Verified with the repo's own tooling: ./configure --help lists the new option between --enable-unittests and --openssl-crypto-library; --enable-virtual-transport --dryrun runs "cmake ... -DENABLE_VIRTUAL_TRANSPORT=1"; --disable-virtual-transport is inverted to =0 by the disabler machinery; regenerating the options list from a stock CMakeCache shows no missing SRT option in cmake_options.
The ENABLE_VIRTUAL_TRANSPORT option and its TestVirtualTransport unit test suite were built by no CI configuration, so the gate-ON code path would rot silently between the stock workflows, which all configure with the option at its default OFF and never compile the suite. Added ubuntu-virtual-transport.yml, a single lean Linux job following the ubuntu-c++11.yml conventions (same runner and step layout; the triggers additionally list the fork development branch, so the gate-ON build runs on every push to that branch as well as on the master and dev events the stock workflows fire on). It configures the C++11 build with unit tests and testing enabled plus ENABLE_VIRTUAL_TRANSPORT=ON and warnings-as-error (also exercising the heavy logging path that comes with ENABLE_DEBUG), then runs the FULL test-srt suite - VT-gated code paths are exercised only here, since the stock Linux C++11 workflow builds with the option OFF. A dedicated file was preferred over growing the ubuntu-c++11 matrix because the repo keeps one workflow file per build configuration and a matrix dimension would double that workflow's jobs and drag its coverage/codecov baggage along.
API-socket-options.md: full sections for SRTO_SKIP_INDUCTION and SRTO_INITIAL_RTT in the established format. SRTO_SKIP_INDUCTION is documented as a pre-restriction boolean that makes a caller send the HSv5 Conclusion immediately with cookie 0, rejected together with SRTO_RENDEZVOUS and immutable past connect/listen; on a listener it accepts a Conclusion-first handshake, with the cookie check bypassed only for requests arriving over the listener's virtual transport channel (a plain UDP listener keeps the stock validation, and a plain UDP caller is rejected at connect time). SRTO_INITIAL_RTT is documented as a pre-restriction integer (ms, default 0 disabled, nonzero 1..30000) seeding mean RTT and variance=mean/4 (RFC 6298 ratio) ahead of the first live ACK/ACKACK sample, suppressing the per-peer-address cache lookup at connection completion, and inherited by accepted sockets. The version cells of both option rows and of the ENABLE_VIRTUAL_TRANSPORT build option stay blank pending an upstream release that ships them. API-functions.md: a Virtual Transport section (index table plus function reference) covering srt_attach_virtual_transport, srt_detach_virtual_transport, srt_virtual_transport_inject and srt_virtual_transport_release, following the page's per-function format with Returns/Errors tables and the SRTVirtualTransportSendFunc callback type definition. The send callback contract is spelled out: fire and forget exactly like a sendto() on a UDP socket (SRT neither retries nor otherwise reacts to a short or zero return), invocations run on internal SRT threads and are serialized, and after detach returns no further invocations happen - closing is not a substitute for detaching, since the shutdown still flows through the callback. Forking a process that uses a socket with an attached virtual transport is not supported: the transport's condition-variable wakeup, pending-datagram queue and other internal state shared between threads are not reset in the child, so the behavior of the child process is undefined. Attached sockets use a dedicated multiplexer each with no OS socket, and the platform support is spelled as Linux and Emscripten. build-options.md: the ENABLE_VIRTUAL_TRANSPORT row in the options table and a detail section stating what the seam enables, the platform restriction, the C++11 minimum (a lower USE_CXX_STD is rejected at configure time), the OFF default, and that an OFF build is stock.
postConnect cleared m_bConnecting before setting m_bConnected, and CUDTSocket::getStatus() synthesizes SRTS_BROKEN for a socket whose status is still SRTS_CONNECTING while both the connecting and connected flags are clear (the connect-timeout report). A thread polling srt_getsockstate() could therefore observe a socket as BROKEN while its handshake was completing: reorder the stores so the connected flag is published before the connecting flag is cleared, making that flag combination unreachable during the regular CONNECTING -> CONNECTED transition. No functional path reads m_bConnecting between the two stores; the socket's own completion logic and all error paths are unchanged. Demonstrated with a caller-listener pair over the virtual transport seam (skip-induction, non-blocking caller) and a concurrent status poller: with the window artificially widened, the original order produced a spurious BROKEN on 1898 of 2000 connections (all of which completed); with the fix the report is gone. The false report was observed killing live sessions in a production WebTransport gateway, whose session ticker polls every socket's status every ~2 ms and terminates a session on a single BROKEN sample; TSan-slowed CI runners stretched the window enough to hit it roughly once per four 50-viewer test runs. Sibling windows exist while srt_connect() itself is on the caller's stack (the status is set to CONNECTING in CUDTUnited::connectIn before startConnect() publishes m_bConnecting, and an unbound caller passes through SRTS_OPENED on the way). They are not addressed here: they are observable only by a poller that runs during the srt_connect() call, not by one that starts polling after it returns. Upstreamable as is.
sendCtrl(UMSG_LOSSREPORT) read the receiver loss list without m_RcvLossLock in two places: the getLossLength() test guarding the no-argument branch, which then took the lock only around getLossArray(), and the getLossLength() that feeds updateNAKInterval after the branch, which ran unlocked in every path. The receiver loss list is mutated concurrently by the TSBPD thread, so those unlocked reads raced the mutations. Both branches now snapshot the loss length under m_RcvLossLock, the no-argument branch tests the snapshot instead of the shared state, and the NAK interval update runs on the snapshot, so no loss-list state is read outside the lock.
checkBrokenSockets() decides whether a lingering closed socket may be qualified for deletion by reading m_pSndBuffer and calling getCurrBufSize() on it. On a socket whose connection was completing at close time, prepareBuffers() publishes m_pSndBuffer and m_pRcvBuffer on the connecting thread, so the GC thread could read the pointer and walk the buffer while the publication was still unsynchronized. The GC read is now taken under m_RcvBufferLock, and prepareBuffers() publishes both buffers under the same lock, so the GC sees the buffers only fully published.
readMessage() logs a warning when it finds nothing readable and no out-of-order packet pending, suggesting the caller ignored the isRcvDataReady() result. The condition is also reached through benign readiness/read interleavings, where a Warn-level line per read is log noise rather than a fault indication. Demoted to Debug.
The virtual transport API (srt_attach_virtual_transport and sibling functions) and the SRTO_SKIP_INDUCTION/SRTO_INITIAL_RTT options were previously declared only under SRT_ENABLE_VIRTUAL_TRANSPORT, so applications compiled against a seam-enabled library failed to even link against a stock build, and every libsrt.so.1 carried a different symbol set. Following the ENABLE_BONDING precedent: the declarations and enum values are now unconditional, while the implementations remain gated - in builds without the seam the functions fail (attach returns NULL, the rest return -1/SRT_EINVPARAM) and the two options are rejected as unknown.
The virtual transport symbols now exist in every build, and in builds without ENABLE_VIRTUAL_TRANSPORT they must fail cleanly: attach returns NULL, detach and inject return -1/SRT_EINVPARAM, and the SRTO_SKIP_INDUCTION/SRTO_INITIAL_RTT options are rejected as unknown. These tests exercise those paths in the stock build; in seam-enabled builds the file compiles empty and the positive paths remain covered by the TestVirtualTransport suite.
The virtual transport functions and the SRTO_SKIP_INDUCTION and SRTO_INITIAL_RTT options are now declared in every build; in builds without ENABLE_VIRTUAL_TRANSPORT they fail at runtime instead of being absent. The API, socket option, and build option documents now state the runtime failure behavior and keep the build flag scoped to where the feature actually works.
The workflow fired on the quicsrt branch of the fork, which does not exist upstream; master and dev cover the upstream targets.
Comments and test payloads referenced the QuicSRT product and the srt-rs fork by name. For the upstream tree the same points are made with neutral wording: gateway examples speak of a generic WebTransport gateway, the RTT variance ratio cites RFC 6298, and the skip-induction cookie behavior is described by its mechanism instead of the fork it was borrowed from. No code change - comments and test payload strings only.
The two files added by the virtual transport seam carried several spots that violate the repository .clang-format (an empty destructor body, a line-wrap in the WireSend matcher, aligned member declarations in the test fixture, and joined assertion lines). Formatted with clang-format; no code change.
The identifier and payload string "warmup" in the detach test trip the codespell dictionary (upstream CI runs it with the repository config); renamed to warm_up / "vt-detach warm-up", which upstream dictionaries accept. No change to what the test does.
The unconditional SOURCES list of the unit tests compiles test_virtual_transport_stock.cpp on every platform, and the plain #include <arpa/inet.h> before the in-file guard does not exist under the MSVC Windows build (which runs the unit tests in CI). The identifiers the file needs (sockaddr_in, inet_addr, htons, AF_INET) already arrive through srt.h -> platform_sys.h on both platform families, so the include is simply removed.
The trailing "(ENABLE_VIRTUAL_TRANSPORT)" notes on the now unconditional SRTO_SKIP_INDUCTION and SRTO_INITIAL_RTT read like compile-time gating of the enumerators; reworded to "(requires ENABLE_VIRTUAL_TRANSPORT)" to state the build option the feature needs, matching the availability wording of the API block.
Three accuracy fixes from the review round: the getStatus() timeout check now documents that its flag reads are order-sensitive (m_bConnecting must be read before m_bConnected, mirroring the publication order postConnect was fixed to use); the skip-induction cookie bypass no longer claims the expected cookie is "recorded as whatever the peer has sent" - the check is simply skipped, nothing is recorded; and the SRTO_INITIAL_RTT variance comments state the actual arithmetic (RTO = SRTT + 4*RTTVAR = 2*SRTT, the RFC 6298 RTO shape) instead of a non-existent "RFC 6298 variance/mean ratio", noting SRT's own initial estimate uses mean/2. The srt_virtual_transport_release() doc comment additionally states that the send callback keeps firing after the handle is released until the socket is closed or the transport is detached - the rule existed only in the API document, not in the header a C application reads.
The synchronous-close wait loop (SRT_ENABLE_CLOSE_SYNCH, used with SRTO_SNDSYN) read m_pSndBuffer and its occupancy unlocked, while a connection completing on another thread publishes those pointers in prepareBuffers under m_RcvBufferLock - the same publication race checkBrokenSockets was fixed against, left open in this one remaining reader. The loop now snapshots the pointer and the buffered count under m_RcvBufferLock at the top of each iteration and does its waiting unlocked, so the publication is never observed torn. The consequence was benign before (the count is a single int and the loop exits through other checks), but the read is now uniform with the GC scan.
The SRTO_SKIP_INDUCTION section now documents the PBKEYLEN consequence of skipping the Induction phase: without the listener's key-length advertisement a mismatch that stock auto-resolves during the handshake instead fails at the key-material exchange, so both endpoints should use a consistent SRTO_PBKEYLEN. The attach error table gains the SRT_ENOBUF row for the allocation-failure path, and the SRTO_INITIAL_RTT description states the RTO arithmetic precisely instead of citing a non-existent RFC 6298 variance/mean ratio.
…s on The ENABLE_VIRTUAL_TRANSPORT C++11 floor was only checked when USE_CXX_STD was explicitly defined; configured without it, the build compiled with the compiler's default standard and the floor was never enforced. USE_CXX_STD now defaults to 11 when the seam is enabled and no standard was requested, mirroring the ENABLE_STDCXX_SYNC pattern. A local VT build with no USE_CXX_STD now compiles all library sources as C++11 and runs the full suite (the SharedMutex tests, compiled only below C++17, included).
…ipts The workflow re-implemented the prepare/configure/build/test steps inline and ran ./test-srt directly, where every other Linux workflow delegates to scripts/workflows/build and tests through ctest. Switched to the shared scripts with the CMake passthrough for the option (-- -DENABLE_VIRTUAL_TRANSPORT=ON), the form ubuntu-mingw.yml already uses, so the job inherits upstream's exact flag handling and core-dump processing and cannot drift from the other workflows. The configure line was verified locally.
inet_addr is deprecated on the Windows SDKs (C4996) and this test file compiles on the MSVC unit-test CI; inet_pton is warning-free everywhere. Filling the address is the only change.
The skip-induction positive test passed even if the caller regressed to sending an Induction first, because the virtual listener answers stock inductions as well. The Wire harness now captures the first datagram a caller sends, and the test parses it as a CHandShake to assert that the very first flight is a Conclusion with cookie 0 - the option's defining behavior. Verified falsifiable by flipping the expected request type to Induction and watching the test fail; the stock test's Induction pin was flipped the other way and failed as well.
CVirtualTransport::receive() took the payload source pointer as &dg.data[HDR_SIZE]; for a header-only datagram that is operator[](size()) on the datagram vector - a one-past-the-end element access, undefined behavior that hardened libstdc++ builds (Ubuntu 26.04/GCC 15 ship assertions on) trap with an abort in the receive worker. Caught by the fork CI rehearsal on the InjectInvalidSizeRejected test. The pointer is now derived through data() + HDR_SIZE, which is well-defined for the one-past-the-end case; the zero-length memcpy from it was always legal. Verified with a local -D_GLIBCXX_ASSERTIONS build: the abort is gone and the full virtual transport suite passes.
|
"Marking ready for review. Full CI validated on the fork (see CI notes); the three red checks reproduce identically on unmodified upstream master." |
|
Ok, first things first: As this would have to be a new feature, it cannot be drawn off The As for this solution itself, it's also not acceptable in this form by the following reasons:
|
|
Thank you for the detailed review — this is very helpful. Before I rework it, here's my understanding of the target design, so please
One thing I'd like to confirm before I implement: datagram injection. And one scoping question: the PR also adds |
|
Ok, so responding to your questions:
The most important now is that the multiplexer sharing works correctly
As I said, from the design point of view both are acceptable. Important for
Ok, note that we have also considered using conclusion-only handshakes
Imagine this the same way as you would replace the UDP socket with a What you are providing for SRT is a replacement for UDP socket; normally So, you likely need:
(Not sure about sender readiness check - likely it should suffice if this SRT only needs the right callbacks provided to be able to use this thing. In other words, the mechnism to "deliver" the wrapped packet by pushing
That's a kind of problem. SRT does have a mechanism for this - see the CCache NOTE: this design is a bit flawed (I have some plan prepared to fix it, but What I had in mind to redesign it is that a single facility to send to
If you decide to do it this way, at least you'll get rid of the problem of |
What this adds
SRT sockets can now exchange their datagrams through an owner-supplied
userspace transport (for example WebTransport/QUIC) instead of an OS UDP
socket, with no change to the behavior of regular UDP sockets in the
same process. Everything above the multiplexer channel — handshake, ARQ,
crypto, TSBPD — runs unchanged; the feature is gated behind the CMake
option
ENABLE_VIRTUAL_TRANSPORT(OFF by default, Linux and Emscripten).Motivating use case: serving SRT sessions to browser/WASM clients over
WebTransport, where the browser hands datagrams to the application and
there is no UDP socket to bind.
The seam
srt_attach_virtual_transport()wires an unbound socket to anowner-supplied send callback; outgoing datagrams are delivered to it
as one contiguous network-order buffer, exactly the bytes a UDP
sendto()would have emitted.srt_virtual_transport_inject()feeds received datagrams back in,thread-safe, from any thread.
srt_detach_virtual_transport()/srt_virtual_transport_release()manage lifetime; the transport object is reference-counted so the
caller's handle stays valid concurrently with socket closure.
CChannel; every attached socket owns adedicated multiplexer, so stock UDP sockets are unaffected.
Two accompanying options
SRTO_SKIP_INDUCTION— conclusion-first handshakes. The enclosingtransport already authenticates the peer (e.g. WebTransport TLS), so
the Induction round trip is redundant. A caller with this option sends
the Conclusion immediately with cookie 0; a listener accepts it only
when the request arrived over a virtual-transport channel — a plain
UDP listener keeps the full cookie validation.
SRTO_INITIAL_RTT— seed the RTT estimator from the enclosingtransport's measured RTT (e.g. QUIC's smoothed RTT) until the first
live sample. Skipping Induction removes the round trip that normally
delivers the first sample, so the two options are designed to
accompany each other.
Included stock fixes
Four independent fixes to existing code, found while stress-testing this
work; each is self-contained and useful on its own:
SRTS_BROKENwindow inpostConnect(status poller could seeBROKEN while the handshake was completing)
pattern in the CLOSE_SYNCH wait)
We're happy to split these into separate PRs first if that's easier
to review — say the word.
API shape (open to discussion)
The new symbols follow the
ENABLE_BONDINGprecedent: declared andlinkable in every build; in builds without the seam they fail at runtime
(attach returns NULL, the int-returning functions -1/
SRT_EINVPARAM,the options rejected as unsupported). This keeps a single
libsrt.soABI stable across build configurations. If maintainers prefer a
different gating scheme, happy to adjust.
Testing
(rendezvous, stock-UDP, attach-after-bind), detach under live traffic,
queue pressure, and a wire-level assertion that the first skip-induction
flight is a cookie-0 Conclusion.
symbols and options when the seam is compiled out.
C++11-strict.
Reviewing
The commit series is structured feature-first, then the stock fixes,
then review polish; the significant files are
srtcore/virtual_transport.{h,cpp}(the transport object),
srtcore/channel.*(the seam),srtcore/api.cpp(attach/detach plumbing),
srtcore/core.cpp(handshake and options),test/test_virtual_transport*.cpp, and thedocs/APIsections.CI notes
Full CI was validated on a fork rehearsal: every workflow that can pass, passes;
plus a dedicated virtual-transport workflow that builds with
ENABLE_VIRTUAL_TRANSPORT=ON, C++11, warnings-as-errors, and runs thecomplete test suite (325 tests, including the 16-case virtual-transport
suite and the 4-case stock-build rejection suite).
Three checks fail on the fork's runners for reasons unrelated to this
change, each reproduced on unmodified upstream master in
this probe PR:
Linux C++11, ARM job — flaky upstream test; upstream's own runsfrom Oct 9, Oct 15, and Oct 30 (twice) show the same failures on
Haivision's own runners.
QEMU s390x— fails at the configure step, before any build;runner infrastructure.
ABI checks— GitHub is migratingubuntu-latestfrom Ubuntu24.04 to 26.04 gradually between Oct 19 and Nov 19, 2026
(runner-images #14748).
Runs that land on the new image get CMake 4, which refuses to configure
the 2021-era
v1.5.0baseline tag (itscmake_minimum_requiredpredates CMake 3.5). Haivision's own Oct 30 run passed on the old
image; the same unmodified code fails on the new one. This affects
every
ubuntu-latestworkflow in this repository as the rolloutcompletes — not just this PR.
One real portability bug was found thanks to the new image and is fixed
in this PR: a one-past-the-end
std::vectoraccess in the virtualtransport receive path, trapped by GCC 15's hardened libstdc++ (default
on 26.04). The suite passes under
-D_GLIBCXX_ASSERTIONS.