diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e0483b14..b68bfb2be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,7 @@ jobs: - clippy - docs - loom + - valgrind steps: - run: exit 0 @@ -67,6 +68,28 @@ jobs: run: cargo hack test --each-feature working-directory: tests-build + valgrind: + name: valgrind + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable + + - name: Install Valgrind + run: | + sudo apt-get update -y + sudo apt-get install -y valgrind + + # Compile tests + - name: cargo build + run: cargo build --features rt-net --bin test-mem + working-directory: tests-integration + + # Run with valgrind + - name: Run valgrind + run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-mem + test-unstable: name: test tokio full --unstable runs-on: ${{ matrix.os }} diff --git a/tests-integration/Cargo.toml b/tests-integration/Cargo.toml index 105b9c618..0b98ddc54 100644 --- a/tests-integration/Cargo.toml +++ b/tests-integration/Cargo.toml @@ -5,7 +5,17 @@ authors = ["Tokio Contributors "] edition = "2018" publish = false +[[bin]] +name = "test-cat" + +[[bin]] +name = "test-mem" +required-features = ["rt-net"] + [features] +# For mem check +rt-net = ["tokio/rt", "tokio/rt-multi-thread", "tokio/net"] + full = [ "macros", "rt", @@ -23,6 +33,4 @@ rt-multi-thread = ["rt", "tokio/rt-multi-thread"] tokio = { path = "../tokio" } tokio-test = { path = "../tokio-test", optional = true } doc-comment = "0.3.1" - -[dev-dependencies] futures = { version = "0.3.0", features = ["async-await"] } diff --git a/tests-integration/src/bin/test-mem.rs b/tests-integration/src/bin/test-mem.rs new file mode 100644 index 000000000..98aa971ac --- /dev/null +++ b/tests-integration/src/bin/test-mem.rs @@ -0,0 +1,21 @@ +use futures::future::poll_fn; + +fn main() { + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .enable_io() + .build() + .unwrap(); + + rt.block_on(async { + let listener = tokio::net::TcpListener::bind("0.0.0.0:0").await.unwrap(); + tokio::spawn(async move { + loop { + poll_fn(|cx| listener.poll_accept(cx)).await.unwrap(); + } + }); + }); + + std::thread::sleep(std::time::Duration::from_millis(50)); + drop(rt); +} diff --git a/tokio/CHANGELOG.md b/tokio/CHANGELOG.md index 499bdd024..7f283017a 100644 --- a/tokio/CHANGELOG.md +++ b/tokio/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.1.1 (January 29, 2021) + +Forward ports 1.0.3 fix. + +### Fixed +- io: memory leak during shutdown (#3477). + # 1.1.0 (January 22, 2020) ### Added @@ -48,6 +55,11 @@ [#3457]: https://github.com/tokio-rs/tokio/pull/3457 [#3458]: https://github.com/tokio-rs/tokio/pull/3458 +# 1.0.3 (January 28, 2020) + +### Fixed +- io: memory leak during shutdown (#3477). + # 1.0.2 (January 14, 2020) ### Fixed diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index c52342350..1e719a235 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -8,12 +8,12 @@ name = "tokio" # - README.md # - Update CHANGELOG.md. # - Create "v1.0.x" git tag. -version = "1.1.0" +version = "1.1.1" edition = "2018" authors = ["Tokio Contributors "] license = "MIT" readme = "README.md" -documentation = "https://docs.rs/tokio/1.1.0/tokio/" +documentation = "https://docs.rs/tokio/1.1.1/tokio/" repository = "https://github.com/tokio-rs/tokio" homepage = "https://tokio.rs" description = """ diff --git a/tokio/src/io/driver/registration.rs b/tokio/src/io/driver/registration.rs index 93125814f..145122459 100644 --- a/tokio/src/io/driver/registration.rs +++ b/tokio/src/io/driver/registration.rs @@ -205,6 +205,19 @@ impl Registration { } } +impl Drop for Registration { + fn drop(&mut self) { + // It is possible for a cycle to be created between wakers stored in + // `ScheduledIo` instances and `Arc`. To break this + // cycle, wakers are cleared. This is an imperfect solution as it is + // possible to store a `Registration` in a waker. In this case, the + // cycle would remain. + // + // See tokio-rs/tokio#3481 for more details. + self.shared.clear_wakers(); + } +} + fn gone() -> io::Error { io::Error::new(io::ErrorKind::Other, "IO driver has terminated") } diff --git a/tokio/src/io/driver/scheduled_io.rs b/tokio/src/io/driver/scheduled_io.rs index 75d562327..71864b3e6 100644 --- a/tokio/src/io/driver/scheduled_io.rs +++ b/tokio/src/io/driver/scheduled_io.rs @@ -355,6 +355,12 @@ impl ScheduledIo { // result isn't important let _ = self.set_readiness(None, Tick::Clear(event.tick), |curr| curr - mask_no_closed); } + + pub(crate) fn clear_wakers(&self) { + let mut waiters = self.waiters.lock(); + waiters.reader.take(); + waiters.writer.take(); + } } impl Drop for ScheduledIo { diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index 7273a9dbc..2d1911e30 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://docs.rs/tokio/1.1.0")] +#![doc(html_root_url = "https://docs.rs/tokio/1.1.1")] #![allow( clippy::cognitive_complexity, clippy::large_enum_variant,