This extends the public quinn API to offer support for writing owned buffers.
Besides the universal `write_chunks` API which supports a variable amount
of buffers the API also offers convenience methods for writing either a
single buffer completely or an arbitrary amount of buffers completely.
This change adds an optional feature which allows to track how much
time was spent inside locks on connections, as well as which other
tasks held the lock (in case there is lock contention).
This makes it easier to determine which code path are currently not
as non-blocking as they should be.
Examples of output:
```
Feb 04 22:05:42.293 WARN quinn::mutex: Utilizing the connection for poll took 165.2318ms
```
```
Feb 04 22:05:42.128 WARN quinn::mutex: Locking the connection for poll took 1.1284ms. Last owners: [("SendStream::poll_write", 7.6µs), ("SendStream::poll_write", 46.4µs), ("SendStream::poll_write", 792.8µs), ("drop", 100ns), ("clone", 100ns), ("OpenUni::next", 1.3µs), ("clone", 295.2µs), ("poll", 14.1546ms), ("drop", 100ns), ("drop", 100ns), ("drop", 439.2µs), ("poll", 9.7657ms), ("clone", 0ns), ("clone", 0ns), ("clone", 100ns), ("connecting", 308.3µs), ("poll", 30.402ms), ("poll", 4.1406ms), ("clone", 100ns)]
```
Add a has_pending_retransmits method to
quinn_proto::connection::Connection and use it inside of
quinn::RecvStream::poll_read_generic to decide if we should wake up the
connection driver.
Previously, we limited the number of streams that could be opened by
the peer but not `accept`ed by the application. However, to guarantee
bounded resource use, applications will typically want to limit the
number of streams they process concurrently. While this could
be *approximately* implemented at the application layer by controlling
calls to accept, that approach has a significant drawback: If streams
are slow to process, the worst-case per-stream latency observed by a
peer who opens the maximum number of streams can be arbitrarily bad,
because streams may be opened above the limit the local application is
willing to process. Reducing the number of unaccepted streams
tolerated can reduce the proportion of streams affected, but reducing
it too low will increase the number of round trips required to open
any given number of streams, increasing average latency significantly.
As a side benefit, this reduces the effort needed for applications to
limit concurrency to a fixed quantity, which is expected to be the
overwhelmingly common case. Should a use case for dynamic concurrency
limits arise, we can expose a setter.
This is a follow-up for #943. When a socket is bound to a wildcard
IP address, sending the outgoing IP might use a different source IP
address than the one the packet was received on, since the OS might
not be able to identify the necessary route. This would lead packets
not allowing to reach the client.
This change adds a setting which will set an explicit source address
in all outgoing packets. The source address which will be used is
the local IP address which was used to receive the initial incoming
packet.
This splits out the platform/socket parts for UDP GSO support
from #953 to reduce the amount of code to review.
This change mainly implements setting the segment size
socket option, and adds a runtime detection mechanism for
GSO support.
So far those haven't been consumable by applications.
This makes the struct public, and adds an accessor
for `quinn_proto::Connection` and `quinn::Connection`.
The outgoing cmsgs can't be uninit because it breaks `CMSG_NXTHDR`.
The function will try to access the next cmsg in the buffer at
https://github.com/rust-lang/libc/blob/ae65df55fe2d0e1348fed575bfc8c0c3e4744c65/src/unix/linux_like/linux/mod.rs#L2585-L2589
This will yield an undefined result, and can let `CMSG_NEXT` return `None`.
This prevents more than 1 CMSG to be encoded. 0-initializing the
messages makes sure a length of 0 is read, and fixes the issue.
In addition this change improves an assert in the cmsg encoder, which
makes it easier to determine the required CMSG space.
When a listener is bound to multiple network interfaces (e.g. `::0`),
it is not obvious which IP the peer used to send a packet. We however
might need this information to send packets back to the peer with the
same source address.
This problem is described in #508.
This change makes the destination IP address which was used to send
the initial packet available in the `Conneting` and `Connection` types.
The information is far available only on Linux due to missing test on
other platforms.
This extends the "cargo bench" benchmark to test transmission of data
on multiple streams in parallel.
Ideally we would want all transmission to take a similar amount of time
(bandwidth shared fairly), and the overall throughput to stay the same
or increase (but never degrade) when multiple streams are in use.
The benchmark can not show the fairness aspect very well, since it only
prints an overall throughput. But it shows that performance with
multiple streams stays similar.
Output:
```
running 4 tests
test large_data_10_streams ... bench: 180,761,260 ns/iter (+/- 12,090,679) = 58 MB/s
test large_data_1_stream ... bench: 18,459,500 ns/iter (+/- 3,804,219) = 56 MB/s
test small_data_100_streams ... bench: 4,600,890 ns/iter (+/- 373,842)
test small_data_1_stream ... bench: 71,019 ns/iter (+/- 34,298)
```
I also increased the size of the LARGE_DATA payload from 128kB to
1MB, since 128kB didn't show quinn peak performance.