Skip to content

Windows: cache the stdio write mode for the duration of a lock session - #159562

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
Joel-Wwalker:154071-stdio-handle-cache
Sep 16, 2026
Merged

rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
Joel-Wwalker:154071-stdio-handle-cache

Conversation

@Joel-Wwalker

@Joel-Wwalker Joel-Wwalker commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

On Windows, every write to stdout/stderr calls GetStdHandle and GetConsoleMode (and GetConsoleOutputCP for consoles) to decide how to write. For bulk writes through a locked handle that is most of the work; #154071 measured 14% of CPU time in is_console alone.

This caches the handle and the console verdict in the sys-level Stdout/Stderr and re-queries when a new lock session begins, as suggested in the issue. Holding a StdoutLock pins the stream; unlocked writes acquire the lock per call, so they re-query per write exactly as before, and SetStdHandle calls made between lock sessions keep working (the #40490 behavior is preserved).

Numbers from the issue's workload shape (16 KiB chunks to NUL, 4 GiB total, Windows 11):

mode before after
stdout().lock() 0.478 s (8,561 MiB/s) 0.120 s (34,350 MiB/s)
unlocked 0.467 s 0.293 s

The unlocked case also improves because one implicit lock session can contain several sys-level writes when LineWriter splits a chunk at newlines.

The console path cannot run in CI, so it was verified under a pseudoconsole: several writes through one lock session on a CP437 console came through WriteConsoleW intact (non-ASCII included), and after SetConsoleOutputCP plus a new lock session the refresh picked up the change.

The refresh hook is a small #[cfg(windows)] shim on StdoutRaw/StderrRaw; happy to change it to a method on every platform's sys type instead if that is preferred.

Fixes #154071

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 19, 2026
@rustbot

rustbot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

r? @aapoalas

rustbot has assigned @aapoalas.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 13 candidates
  • Random selection from 7 candidates

Comment thread library/std/src/io/stdio.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 23, 2026
@rustbot

rustbot commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@Joel-Wwalker

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 25, 2026
@rust-bors

This comment has been minimized.

@aapoalas aapoalas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for the good work <3

@bors squash

View changes since this review

@rust-bors

This comment has been minimized.

@rust-bors

rust-bors Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

🔨 2 commits were squashed into f05562d.

@rust-bors
rust-bors Bot force-pushed the 154071-stdio-handle-cache branch from 8fa6c14 to f05562d Compare August 19, 2026 19:23
@rustbot

This comment has been minimized.

@aapoalas

This comment was marked as outdated.

@rust-bors

rust-bors Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

📋 This PR cannot be approved because it has merge conflicts. Please resolve the merge conflicts by rebasing, and try again.

@aapoalas

Copy link
Copy Markdown
Contributor

Oop, right: we have a merge conflict! Sorry @Joel-Wwalker could you resolve that? r=me once CI passes <3

@bors delegate+

@rust-bors

rust-bors Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

✌️ @Joel-Wwalker, you can now approve this pull request!

If @aapoalas told you to "r=me" after making some further change, then please make that change and post @bors r=aapoalas.

View changes since this delegation.

* Windows: cache the stdio write mode for the duration of a lock session

Every write to stdout/stderr re-queried GetStdHandle and GetConsoleMode
(and GetConsoleOutputCP for consoles) to decide how to write, which
dominates bulk writes through a locked handle. Cache the handle and the
console verdict in the sys-level Stdout/Stderr and re-query when a new
lock session begins, so SetStdHandle calls made between lock sessions
keep working.
* Gate the stdio lock-session refresh to Windows

On targets where refresh is a no-op, lock() now compiles to exactly
the code it had before, instead of relying on the borrow-flag dance
optimizing out.
@Joel-Wwalker
Joel-Wwalker force-pushed the 154071-stdio-handle-cache branch from f05562d to 0154b97 Compare September 15, 2026 18:24
@rustbot

rustbot commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@Joel-Wwalker

Copy link
Copy Markdown
Contributor Author

Rebased. The conflict was #160953 cleaning up return ret; in the same hunk; no other changes.

@bors r=aapoalas rollup=always

@rust-bors

rust-bors Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 0154b97 has been approved by aapoalas

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 15, 2026
rust-bors Bot pushed a commit that referenced this pull request Sep 16, 2026
…uwer

Rollup of 7 pull requests

Successful merges:

 - #161873 (move bug and span_bug macros to rustc_span)
 - #162584 (Use File::*lock*() in rustc_data_structures::flock when possible)
 - #162704 (Parser: Uninterpolate when checking for const closures, try bikeshed blocks & in relevant diagnostic code)
 - #162787 (Fix suggestions for names captured by formatting macros)
 - #159562 (Windows: cache the stdio write mode for the duration of a lock session)
 - #162813 (Pre lint port cleanups)
 - #162822 (PassWrapper: adapt for removal of EABIVersion arg in LLVM 24)
@rust-bors
rust-bors Bot merged commit 556c0e6 into rust-lang:main Sep 16, 2026
13 checks passed
@rustbot rustbot added this to the 1.100.0 milestone Sep 16, 2026
rust-bors Bot pushed a commit that referenced this pull request Sep 16, 2026
Rollup merge of #159562 - Joel-Wwalker:154071-stdio-handle-cache, r=aapoalas

Windows: cache the stdio write mode for the duration of a lock session

On Windows, every write to stdout/stderr calls `GetStdHandle` and `GetConsoleMode` (and `GetConsoleOutputCP` for consoles) to decide how to write. For bulk writes through a locked handle that is most of the work; #154071 measured 14% of CPU time in `is_console` alone.

This caches the handle and the console verdict in the sys-level `Stdout`/`Stderr` and re-queries when a new lock session begins, as [suggested in the issue](#154071 (comment)). Holding a `StdoutLock` pins the stream; unlocked writes acquire the lock per call, so they re-query per write exactly as before, and `SetStdHandle` calls made between lock sessions keep working (the #40490 behavior is preserved).

Numbers from the issue's workload shape (16 KiB chunks to `NUL`, 4 GiB total, Windows 11):

| mode | before | after |
|---|---|---|
| `stdout().lock()` | 0.478 s (8,561 MiB/s) | 0.120 s (34,350 MiB/s) |
| unlocked | 0.467 s | 0.293 s |

The unlocked case also improves because one implicit lock session can contain several sys-level writes when `LineWriter` splits a chunk at newlines.

The console path cannot run in CI, so it was verified under a pseudoconsole: several writes through one lock session on a CP437 console came through `WriteConsoleW` intact (non-ASCII included), and after `SetConsoleOutputCP` plus a new lock session the refresh picked up the change.

The `refresh` hook is a small `#[cfg(windows)]` shim on `StdoutRaw`/`StderrRaw`; happy to change it to a method on every platform's sys type instead if that is preferred.

Fixes #154071
pull Bot pushed a commit to LeeeeeeM/miri that referenced this pull request Sep 17, 2026
…uwer

Rollup of 7 pull requests

Successful merges:

 - rust-lang/rust#161873 (move bug and span_bug macros to rustc_span)
 - rust-lang/rust#162584 (Use File::*lock*() in rustc_data_structures::flock when possible)
 - rust-lang/rust#162704 (Parser: Uninterpolate when checking for const closures, try bikeshed blocks & in relevant diagnostic code)
 - rust-lang/rust#162787 (Fix suggestions for names captured by formatting macros)
 - rust-lang/rust#159562 (Windows: cache the stdio write mode for the duration of a lock session)
 - rust-lang/rust#162813 (Pre lint port cleanups)
 - rust-lang/rust#162822 (PassWrapper: adapt for removal of EABIVersion arg in LLVM 24)
bjorn3 pushed a commit to rust-lang/rustc_codegen_cranelift that referenced this pull request Sep 18, 2026
…uwer

Rollup of 7 pull requests

Successful merges:

 - rust-lang/rust#161873 (move bug and span_bug macros to rustc_span)
 - rust-lang/rust#162584 (Use File::*lock*() in rustc_data_structures::flock when possible)
 - rust-lang/rust#162704 (Parser: Uninterpolate when checking for const closures, try bikeshed blocks & in relevant diagnostic code)
 - rust-lang/rust#162787 (Fix suggestions for names captured by formatting macros)
 - rust-lang/rust#159562 (Windows: cache the stdio write mode for the duration of a lock session)
 - rust-lang/rust#162813 (Pre lint port cleanups)
 - rust-lang/rust#162822 (PassWrapper: adapt for removal of EABIVersion arg in LLVM 24)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stdout and StdoutLock make unnecessary repeated syscalls via is_console on Windows

3 participants