Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a very minor race condition in cargo fix. #6515

Merged
merged 1 commit into from
Jan 3, 2019

Conversation

ehuss
Copy link
Contributor

@ehuss ehuss commented Jan 3, 2019

There is a very rare race where a "fix" message was getting posted after Message::Finish, and thus being dropped without being printed. This is caused by the diagnostic server disconnecting the client before posting Message::FixDiagnostic. The solution is to keep the client on the line until after the message is posted.

Fixes some errors in fixes_missing_ampersand seen in CI:
https://travis-ci.org/rust-lang/cargo/jobs/474384359
https://travis-ci.org/rust-lang/cargo/jobs/429809800

I also moved the done check to the beginning of the loop because every time the server was shut down it was needlessly trying to parse an empty string (and failing).

@rust-highfive
Copy link

r? @Eh2406

(rust_highfive has picked a reviewer for you, use r? to override)

Copy link
Member

@dwijnand dwijnand left a comment

Choose a reason for hiding this comment

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

LGTM

src/cargo/util/diagnostic_server.rs Show resolved Hide resolved
@Eh2406
Copy link
Contributor

Eh2406 commented Jan 3, 2019

Should this have some kind of test? Not that I know how to make one...

@dwijnand
Copy link
Member

dwijnand commented Jan 3, 2019

r=me, after the test chat.

@alexcrichton
Copy link
Member

To make sure I understand the race here, the problem is that we closed the client connection too early, which allow the client (the cargo fix process) to continue and finish. This in turn then finished the entire compilation queue, posting a Message::Finish. All of that happened before the server thread was able to post FixDiagnostic, causing a message to not get printed. The solution here is to block the client thread until we've actually sent the FixDiagnostic message along its way into a queue?

If that's all the case I think it's fine to avoid a test here, spurious failures are showing we're already covering this :)

if done.load(Ordering::SeqCst) {
break;
}
let mut client = BufReader::new(client);
Copy link
Member

Choose a reason for hiding this comment

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

FWIW with read_to_string the BufReader here is likely no longer necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I looked at that for a bit. It didn't seem too important, but if I understand the code correctly, a bare read_to_string uses a 32-byte buffer whereas BufReader uses an 8k buffer. Does that sound correct? Sometimes it's difficult to trace with various different traits involved. read_to_end_with_reservation doesn't appear to grow its read size, which is a little surprising.

Copy link
Member

Choose a reason for hiding this comment

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

Eh it could go either way I think. read_to_string starts with 32 bytes and exponentially increases (IIRC) and also avoids zeroing and does other fancy tricks. Ideally we'd reuse a buffer here but this isn't really performance sensitive anyway so it doesn't matter too much. Whichever you feel like doing is fine by me!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, indeed, it does 32,32,64,128,256,... (I'm guessing it's this). I'm fine with leaving it as-is, saves a few round trips in the common case (large text messages).

Copy link
Member

Choose a reason for hiding this comment

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

Ah it's actually this one, but it's morally the same thing!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I was just looking at the end of that call chain, because read_to_end_with_reservation does not do any obvious doubling (and the behavior does not appear to be documented anywhere). I'm assuming the call g.buf.reserve(32) goes to RawVec::reserve which goes to reserve_internal with the Amortized strategy to amortized_new_size which does the doubling (and the needed_extra_cap of 32 gets ignored pretty quickly).

Copy link
Member

Choose a reason for hiding this comment

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

er oops, right!

@ehuss
Copy link
Contributor Author

ehuss commented Jan 3, 2019

The test is fixes_missing_ampersand. I can't think of a way to force the race to happen naturally, and it's fairly rare. I ran it for several hours in a loop on my machines, and on CI, and it seems to be fixed. You can simulate the error with this PR and the following:

diff --git a/src/cargo/util/diagnostic_server.rs b/src/cargo/util/diagnostic_server.rs
index e7e1aff1..39140504 100644
--- a/src/cargo/util/diagnostic_server.rs
+++ b/src/cargo/util/diagnostic_server.rs
@@ -266,6 +266,8 @@ impl RustfixDiagnosticServer {
             if let Err(e) = client.read_to_string(&mut s) {
                 warn!("diagnostic server failed to read: {}", e);
             } else {
+                drop(client);
+                std::thread::sleep(std::time::Duration::from_millis(1000));
                 match serde_json::from_str(&s) {
                     Ok(message) => on_message(message),
                     Err(e) => warn!("invalid diagnostics message: {}", e),
@@ -274,7 +276,6 @@ impl RustfixDiagnosticServer {
             // The client should be kept alive until after `on_message` is
             // called to ensure that the client doesn't exit too soon (and
             // Message::Finish getting posted before Message::FixDiagnostic).
-            drop(client);
         }
     }
 }

and run cargo test --test testsuite -- fixes_missing_ampersand which will give a 100% failure rate.

@ehuss
Copy link
Contributor Author

ehuss commented Jan 3, 2019

I understand the race here

Yes, everything you said is correct.

@alexcrichton
Copy link
Member

@bors: r+

@bors
Copy link
Collaborator

bors commented Jan 3, 2019

📌 Commit 7584dce has been approved by alexcrichton

@bors
Copy link
Collaborator

bors commented Jan 3, 2019

⌛ Testing commit 7584dce with merge d4a04da...

bors added a commit that referenced this pull request Jan 3, 2019
Fix a very minor race condition in `cargo fix`.

There is a very rare race where a "fix" message was getting posted *after* `Message::Finish`, and thus being dropped without being printed. This is caused by the diagnostic server disconnecting the client before posting `Message::FixDiagnostic`. The solution is to keep the client on the line until after the message is posted.

Fixes some errors in `fixes_missing_ampersand` seen in CI:
https://travis-ci.org/rust-lang/cargo/jobs/474384359
https://travis-ci.org/rust-lang/cargo/jobs/429809800

I also moved the `done` check to the beginning of the loop because every time the server was shut down it was needlessly trying to parse an empty string (and failing).
@bors
Copy link
Collaborator

bors commented Jan 3, 2019

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing d4a04da to master...

@bors bors merged commit 7584dce into rust-lang:master Jan 3, 2019
bors added a commit to rust-lang/rust that referenced this pull request Jan 4, 2019
Update cargo

24 commits in 0d1f1bbeabd5b43a7f3ecfa16540af8e76d5efb4..34320d212dca8cd27d06ce93c16c6151f46fcf2e
2018-12-19 14:45:14 +0000 to 2019-01-03 19:12:38 +0000
- Display environment variables for rustc commands (rust-lang/cargo#6492)
- Fix a very minor race condition in `cargo fix`. (rust-lang/cargo#6515)
- Add a high-level overview of how `fix` works. (rust-lang/cargo#6516)
- Add dependency `registry` to `cargo metadata`. (rust-lang/cargo#6500)
- Fix fingerprint calculation for patched deps. (rust-lang/cargo#6493)
- serialize version directly (rust-lang/cargo#6512)
- use DYLD_FALLBACK_LIBRARY_PATH for dylib_path_envvar on macOS (rust-lang/cargo#6355)
- Fix error message when resolving dependencies (rust-lang/cargo#6510)
- use PathBuf in cargo metadata (rust-lang/cargo#6511)
- Fixed link to testsuite in CONTRIBUTING.md (rust-lang/cargo#6506)
- Update display of contents of Cargo.toml (rust-lang/cargo#6501)
- Update display of contents of Cargo.toml (rust-lang/cargo#6502)
- Fixup cargo install's help message (rust-lang/cargo#6495)
- testsuite: Require failing commands to check output. (rust-lang/cargo#6497)
- Delete unnecessary 'return' (rust-lang/cargo#6496)
- Fix new unused patch warning. (rust-lang/cargo#6494)
- Some minor documentation changes. (rust-lang/cargo#6481)
- Add `links` to `cargo metadata`. (rust-lang/cargo#6480)
- Salvaged semver work (rust-lang/cargo#6476)
- Warn on unused patches. (rust-lang/cargo#6470)
- don't write a an incorrect rustc version to the fingerprint file (rust-lang/cargo#6473)
- Rewrite `login` and registry cleanups. (rust-lang/cargo#6466)
- [issue#6461] Fix cargo commands list (rust-lang/cargo#6462)
- Restrict registry names to same style as package names. (rust-lang/cargo#6469)
@ehuss ehuss added this to the 1.33.0 milestone Feb 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants