Skip to content

fix(dgw,jetsocat): stop leaking detached tasks - #2148

Merged
Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
alexander-zimmermann:fix/detached-task-leak
Oct 23, 2026
Merged

Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
alexander-zimmermann:fix/detached-task-leak

Conversation

@alexander-zimmermann

Copy link
Copy Markdown
Contributor

ChildTask::detach skipped the abort in Drop by calling mem::forget on the task's JoinHandle. A forgotten handle never gives back its reference to the task, so tokio never frees a detached task once it finishes, and keeps its output too. The HTTP, HTTPS and TCP listeners detach one task per accepted connection, so every connection leaks a few KB for the lifetime of the process. Kubernetes HTTP probes open a new connection each time, so an otherwise idle Gateway grows by about 16 MiB a day until it reaches its memory limit.

detach now marks the task as detached and drops the handle normally, which detaches the task without aborting it. The private copy of ChildTask in jmux-proxy, used by Gateway and Jetsocat, gets the same fix.

Fixes #1973

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

`ChildTask::detach` skipped the abort in `Drop` by calling `mem::forget`
on the task's `JoinHandle`. A forgotten handle never gives back its
reference to the task, so tokio never frees a detached task once it
finishes, and keeps its output too. The HTTP, HTTPS and TCP listeners
detach one task per accepted connection, so every connection leaks a
few KB for the lifetime of the process. Kubernetes HTTP probes open a
new connection each time, so an otherwise idle Gateway grows by about
16 MiB a day until it reaches its memory limit.

`detach` now marks the task as detached and drops the handle normally,
which detaches the task without aborting it. The private copy of
`ChildTask` in `jmux-proxy`, used by Gateway and Jetsocat, gets the
same fix.

Fixes Devolutions#1973

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexander-zimmermann

Alexander Zimmermann (alexander-zimmermann) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Lines changed (against master):

Area Files Added Removed
Production code devolutions-gateway-task/src/lib.rs, jmux-proxy/src/lib.rs 46 22
Tests the same two files, #[cfg(test)] modules 45 0
Other devolutions-gateway-task/Cargo.toml (tokio macros for #[tokio::test]) 3 0

Validation (Linux arm64 container, rust:1.90)

  • The new test in each crate fails on master with left: 2, right: 1: the finished task still holds its output. It passes with the fix. A detach that aborts the task also fails it, at finished_rx. cargo clippy --all-targets -D warnings and cargo fmt --check are clean for both crates.
  • Release build of devolutions-gateway, one HTTP listener on loopback, 3 × 3000 GET /jet/health, one TCP connection per request, VmRSS 2 s after each round:
Connections master this PR
0 26.22 MiB 26.39 MiB
3000 34.45 MiB 26.66 MiB
6000 42.52 MiB 26.68 MiB
9000 50.59 MiB 26.70 MiB

On master, each connection keeps about 2.8 KiB. With the fix, RSS stays flat after the first round.

jmux-proxy keeps its own private copy of ChildTask, so the fix is applied there as well rather than adding a dependency on devolutions-gateway-task.

@CBenoit

Benoît Cortier (CBenoit) commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Thank you very much for this contribution, I appreciate it.
And sorry for the delay: GitHub Issues is not a channel we usually monitor for Devolutions Gateway.

Your PR is minimal and straightforward.
I see we have some duplicated code, but that is not new from this PR, and can be tackled separately (adding a dependency on devolutions-gateway-task is not what I would like right now).

Thanks again!

@CBenoit
Benoît Cortier (CBenoit) merged commit e6587f2 into Devolutions:master Oct 23, 2026
51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Steady ~15 MiB/day memory growth on an idle gateway (2026.2.4, Linux container)

2 participants