-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
rustc_driver: get rid of the extra thread #48575
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I'm a little worried about the portability here but I think we can manage it where a slice of cases can be optimized to not use a separate thread
src/librustc_driver/lib.rs
Outdated
rlim_cur: 0, | ||
rlim_max: 0, | ||
}; | ||
if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be rlimit_stack?
Are you also sure this works on all Unix platforms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, typo. rlimit is a POSIX standard, so it should.
src/librustc_driver/lib.rs
Outdated
|
||
// Bump the soft limit to the smaller of desired stack size and the hard | ||
// limit | ||
rlim.rlim_cur = cmp::min(STACK_SIZE as libc::rlim_t, rlim.rlim_max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the rlim_max is too small, shouldnt a thread be spawned?
src/librustc_driver/lib.rs
Outdated
|
||
// FIXME: Hacks on hacks. If the env is trying to override the stack size | ||
// then *don't* set it explicitly. | ||
if env::var_os("RUST_MIN_STACK").is_none() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this env var is set shouldn't a thread be spawned?
src/librustc_driver/lib.rs
Outdated
@@ -1412,6 +1413,7 @@ fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<as | |||
/// Runs `f` in a suitable thread for running `rustc`; returns a | |||
/// `Result` with either the return value of `f` or -- if a panic | |||
/// occurs -- the panic value. | |||
#[cfg(not(unix))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could a Windows solution be explored as well? It looks like the stack size can be configured in the executable headers as there is an msvc linker flag for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Do you know a way to pass custom linker flags? It doesn't seem to be supported in build scripts.
src/librustc_driver/lib.rs
Outdated
/// Runs `f` in a suitable thread for running `rustc`; returns a | ||
/// `Result` with either the return value of `f` or -- if a panic | ||
/// occurs -- the panic value. | ||
pub fn in_rustc_thread<F, R>(f: F) -> Result<R, Box<Any + Send>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs #[cfg(unix)]
.
Or combine the two functions in the form:
pub fn in_rustc_thread(...) {
const STACK_SIZE: usize = 16 * 1048576;
#[cfg(not(unix))]
{
// use threads
}
#[cfg(unix)]
{
// use rlimits
}
}
src/librustc_driver/lib.rs
Outdated
const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB | ||
|
||
// FIXME: Hacks on hacks. If the env is trying to override the stack size | ||
// then *don't* set it explicitly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this FIXME applies to the new code.
95d750d
to
b11df43
Compare
I think I've addressed all the review comments except support for Windows. As I said inline, I couldn't figure out how I can pass the I guess it's OK because it compiles, but can you also check if I'm doing inline attributes correctly? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could a comment be added to the code as well to indicate why we're sometimes spawning a thread and sometimes not?
For Windows it looks like we'll want to pass -Wl,--stack,N
on MinGW and /STACK:N
on MSVC. Perhaps an unstable crate attribute could be added to src/rustc/rustc.rs
?
src/librustc_driver/lib.rs
Outdated
} else { | ||
rlim.rlim_cur = STACK_SIZE as libc::rlim_t; | ||
// Set our newly-increased resource limit | ||
if libc::setrlimit(libc::RLIMIT_STACK, &rlim) != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If setrlimit
fails perhaps an info!
log statement could be done but we fall back to spawning a thread? I don't personally know the situations where setrlimit
can fail but it seems like one of those APIs that fails in obscure circumstances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to manual there's no way the current implementation can fail, and thus if we detected an error it's either an OS bug or logic bug of this rlimit setting code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately though in practice "POSIX says X" or "the manual says this can't fail" rarely holds up. Functions fail for mysterious reasons that aren't always strictly documented. I think it's best to try to head off any regressions and just fall back to spawning a thread here.
|
||
// FIXME: Hacks on hacks. If the env is trying to override the stack size | ||
// then *don't* set it explicitly. | ||
if env::var_os("RUST_MIN_STACK").is_none() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be checked earlier? If this env var is set I think we'll want to force spawning a thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree: that environment variable only affects stack size of non-main thread. If we can extend the main thread stack, that's all what we need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without moving this check earlier it's breaking existing behavior. Today the stack size of the rustc thread can be controlled by configuring RUST_MIN_STACK
(can be useful for debugging). This commit breaks that ability because setrlimit will probably succeed on Linux.
c012840
to
472fc3f
Compare
src/rustc/rustc.rs
Outdated
|
||
// Set the stack size at link time on Windows. See rustc_driver::in_rustc_thread | ||
// for the rationale. | ||
#[cfg_attr(all(windows, target_env = "msvc"), link_args = "/STACK:16384")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The /STACK option sets the size of the stack in bytes.
16384 bytes seems rather small.
438ed2c
to
06329dc
Compare
I think I have addressed the concerns, and I have also added linker flags for Windows (didn't test on Windows). Also I no longer attempt to Additionally, I modified the CI script to the CI runs benefit from this change. |
Oh hm isn't this missing the point of the original PR now though? Wasn't the intention to avoid the main thread for debugging and such by default? |
Unfortunately setting rlimit after initialization isn't a safe stuff, and instead we now provide a way to get rid of it if the rlimit is configured by the user. Maybe this can be documented somewhere else, but well, we can now achieve the ideal debugging experience by putting one line of shell configuration. |
Ah sorry I'm not too familiar with setrlimit, but what makes it unsafe to extend the main stack? |
Extending the main stack breaks our stack guard page detection. Basically, when you extend the main stack after initialization, the page address stored in the SEGV handler becomes obsolete and we can no longer handle stack overflow as panic. (What we do with stack guard changed a few times, and although currently it isn't UB it's very likely to cause unwanted behavior.) |
Ah ok thanks for the clarification. That strategy though sounded like a pretty good one to me, so maybe an unstable API could be added to libstd for "reset stack guard detection of this thread"? |
Partially addressed the Windows issues. Though, the default stack size is 1MB on Windows so it's more likely to affect users with clippy. It's possible to get the stack size, but it is rather complicated: |
Looks like CI may be failing? |
@alexcrichton Fixed. |
@bors: r+ |
📌 Commit 7db854b has been approved by |
rustc_driver: get rid of the extra thread **Do not rollup** We can alter the stack size afterwards on Unix. Having a separate thread causes poor debugging experience when interrupting with signals. I have to get the backtrace of the all thread, as the main thread is waiting to join doing nothing else. This patch allows me to just run `bt` to get the desired backtrace.
☀️ Test successful - status-appveyor, status-travis |
I wish this was discussed by the compiler team... there are several issues with the approach and I do not believe the motivation is sufficient (debuggers are usually necessary only for LLVM aborts). It's unsound to not spawn a dedicated thread for the compiler because we can't correctly control thread-local resources - ideally they would be allocated in the parent thread, enforcing the fact that other thread-local data cannot possibly use-after-free the resources in their own destructors. cc @rust-lang/compiler |
rustc_driver: Catch ICEs on the main thread too #48575 introduced an optimization to run rustc directly on the main thread when possible. However, the threaded code detects panics when they `join()` to report as an ICE. When running directly, we need to use `panic::catch_unwind` to get the same effect. cc @ishitatsuyuki r? @alexcrichton
There’s one more potential reason to revert this: #52577. |
@ishitatsuyuki would putting this mode under a |
Yes, as it's not really critical. |
Always run rustc in a thread cc @ishitatsuyuki @eddyb r? @pnkfelix [Previously](#48575) we moved to only producing threads when absolutely necessary. Even before we opted to only create threads in some cases, which [is unsound](#48575 (comment)) due to the way we use thread local storage.
Do not rollup
We can alter the stack size afterwards on Unix.
Having a separate thread causes poor debugging experience when interrupting with signals. I have to get the backtrace of the all thread, as the main thread is waiting to join doing nothing else. This patch allows me to just run
bt
to get the desired backtrace.