forked from tokio-rs/tokio
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into feat-mpsc-many-permit
- Loading branch information
Showing
10 changed files
with
380 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ name = "tokio" | |
# - README.md | ||
# - Update CHANGELOG.md. | ||
# - Create "v1.x.y" git tag. | ||
version = "1.35.0" | ||
version = "1.35.1" | ||
edition = "2021" | ||
rust-version = "1.63" | ||
authors = ["Tokio Contributors <[email protected]>"] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
//! Join two values implementing `AsyncRead` and `AsyncWrite` into a single one. | ||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf}; | ||
|
||
use std::io; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
|
||
/// Join two values implementing `AsyncRead` and `AsyncWrite` into a | ||
/// single handle. | ||
pub fn join<R, W>(reader: R, writer: W) -> Join<R, W> | ||
where | ||
R: AsyncRead, | ||
W: AsyncWrite, | ||
{ | ||
Join { reader, writer } | ||
} | ||
|
||
pin_project_lite::pin_project! { | ||
/// Joins two values implementing `AsyncRead` and `AsyncWrite` into a | ||
/// single handle. | ||
#[derive(Debug)] | ||
pub struct Join<R, W> { | ||
#[pin] | ||
reader: R, | ||
#[pin] | ||
writer: W, | ||
} | ||
} | ||
|
||
impl<R, W> Join<R, W> | ||
where | ||
R: AsyncRead, | ||
W: AsyncWrite, | ||
{ | ||
/// Splits this `Join` back into its `AsyncRead` and `AsyncWrite` | ||
/// components. | ||
pub fn into_inner(self) -> (R, W) { | ||
(self.reader, self.writer) | ||
} | ||
|
||
/// Returns a reference to the inner reader. | ||
pub fn reader(&self) -> &R { | ||
&self.reader | ||
} | ||
|
||
/// Returns a reference to the inner writer. | ||
pub fn writer(&self) -> &W { | ||
&self.writer | ||
} | ||
|
||
/// Returns a mutable reference to the inner reader. | ||
pub fn reader_mut(&mut self) -> &mut R { | ||
&mut self.reader | ||
} | ||
|
||
/// Returns a mutable reference to the inner writer. | ||
pub fn writer_mut(&mut self) -> &mut W { | ||
&mut self.writer | ||
} | ||
|
||
/// Returns a pinned mutable reference to the inner reader. | ||
pub fn reader_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { | ||
self.project().reader | ||
} | ||
|
||
/// Returns a pinned mutable reference to the inner writer. | ||
pub fn writer_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { | ||
self.project().writer | ||
} | ||
} | ||
|
||
impl<R, W> AsyncRead for Join<R, W> | ||
where | ||
R: AsyncRead, | ||
{ | ||
fn poll_read( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
buf: &mut ReadBuf<'_>, | ||
) -> Poll<Result<(), io::Error>> { | ||
self.project().reader.poll_read(cx, buf) | ||
} | ||
} | ||
|
||
impl<R, W> AsyncWrite for Join<R, W> | ||
where | ||
W: AsyncWrite, | ||
{ | ||
fn poll_write( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
buf: &[u8], | ||
) -> Poll<Result<usize, io::Error>> { | ||
self.project().writer.poll_write(cx, buf) | ||
} | ||
|
||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { | ||
self.project().writer.poll_flush(cx) | ||
} | ||
|
||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> { | ||
self.project().writer.poll_shutdown(cx) | ||
} | ||
|
||
fn poll_write_vectored( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
bufs: &[io::IoSlice<'_>], | ||
) -> Poll<Result<usize, io::Error>> { | ||
self.project().writer.poll_write_vectored(cx, bufs) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
self.writer.is_write_vectored() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
#![warn(rust_2018_idioms)] | ||
#![cfg(all(feature = "full", target_os = "linux"))] | ||
|
||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
use std::sync::Arc; | ||
use tokio::net::UdpSocket; | ||
|
||
/// Ensure that UDP sockets have functional budgeting | ||
/// | ||
/// # Design | ||
/// Two sockets communicate by spamming packets from one to the other. | ||
/// | ||
/// In Linux, this packet will be slammed through the entire network stack and into the receiver's buffer during the | ||
/// send system call because we are using the loopback interface. | ||
/// This happens because the softirq chain invoked on send when using the loopback interface covers virtually the | ||
/// entirety of the lifecycle of a packet within the kernel network stack. | ||
/// | ||
/// As a result, neither socket will ever encounter an EWOULDBLOCK, and the only way for these to yield during the loop | ||
/// is through budgeting. | ||
/// | ||
/// A second task runs in the background and increments a counter before yielding, allowing us to know how many times sockets yielded. | ||
/// Since we are both sending and receiving, that should happen once per 64 packets, because budgets are of size 128 | ||
/// and there are two budget events per packet, a send and a recv. | ||
#[tokio::test] | ||
async fn coop_budget_udp_send_recv() { | ||
const BUDGET: usize = 128; | ||
const N_ITERATIONS: usize = 1024; | ||
|
||
const PACKET: &[u8] = b"Hello, world"; | ||
const PACKET_LEN: usize = 12; | ||
|
||
assert_eq!( | ||
PACKET_LEN, | ||
PACKET.len(), | ||
"Defect in test, programmer can't do math" | ||
); | ||
|
||
// bind each socket to a dynamic port, forcing IPv4 addressing on the localhost interface | ||
let tx = UdpSocket::bind("127.0.0.1:0").await.unwrap(); | ||
let rx = UdpSocket::bind("127.0.0.1:0").await.unwrap(); | ||
|
||
tx.connect(rx.local_addr().unwrap()).await.unwrap(); | ||
rx.connect(tx.local_addr().unwrap()).await.unwrap(); | ||
|
||
let tracker = Arc::new(AtomicUsize::default()); | ||
|
||
let tracker_clone = Arc::clone(&tracker); | ||
|
||
tokio::task::yield_now().await; | ||
|
||
tokio::spawn(async move { | ||
loop { | ||
tracker_clone.fetch_add(1, Ordering::SeqCst); | ||
|
||
tokio::task::yield_now().await; | ||
} | ||
}); | ||
|
||
for _ in 0..N_ITERATIONS { | ||
tx.send(PACKET).await.unwrap(); | ||
|
||
let mut tmp = [0; PACKET_LEN]; | ||
|
||
// ensure that we aren't somehow accumulating other | ||
assert_eq!( | ||
PACKET_LEN, | ||
rx.recv(&mut tmp).await.unwrap(), | ||
"Defect in test case, received unexpected result from socket" | ||
); | ||
assert_eq!( | ||
PACKET, &tmp, | ||
"Defect in test case, received unexpected result from socket" | ||
); | ||
} | ||
|
||
assert_eq!(N_ITERATIONS / (BUDGET / 2), tracker.load(Ordering::SeqCst)); | ||
} |
Oops, something went wrong.