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

rustc_driver: get rid of the extra thread #48575

Merged
merged 10 commits into from
Apr 4, 2018

Conversation

ishitatsuyuki
Copy link
Contributor

@ishitatsuyuki ishitatsuyuki commented Feb 27, 2018

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.

@rust-highfive
Copy link
Collaborator

r? @petrochenkov

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

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 27, 2018
@ishitatsuyuki
Copy link
Contributor Author

r? @alexcrichton

Copy link
Member

@alexcrichton alexcrichton left a 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

rlim_cur: 0,
rlim_max: 0,
};
if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 {
Copy link
Member

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?

Copy link
Contributor Author

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.


// 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);
Copy link
Member

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?


// 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() {
Copy link
Member

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?

@@ -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))]
Copy link
Member

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.

Copy link
Contributor Author

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.

/// 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>>
Copy link
Member

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
    }
}

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.
Copy link
Member

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.

@ishitatsuyuki ishitatsuyuki force-pushed the unix-no-thread branch 2 times, most recently from 95d750d to b11df43 Compare February 27, 2018 10:30
@ishitatsuyuki
Copy link
Contributor Author

r? @alexcrichton

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 /STACK link flag.

I guess it's OK because it compiles, but can you also check if I'm doing inline attributes correctly?

Copy link
Member

@alexcrichton alexcrichton left a 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?

} else {
rlim.rlim_cur = STACK_SIZE as libc::rlim_t;
// Set our newly-increased resource limit
if libc::setrlimit(libc::RLIMIT_STACK, &rlim) != 0 {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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() {
Copy link
Member

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.

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 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.

Copy link
Member

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.

@ishitatsuyuki ishitatsuyuki 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 Feb 27, 2018
@ishitatsuyuki ishitatsuyuki force-pushed the unix-no-thread branch 2 times, most recently from c012840 to 472fc3f Compare March 4, 2018 01:30
@ishitatsuyuki ishitatsuyuki changed the title rustc_driver: get rid of extra thread on Unix rustc_driver: get rid of the extra thread Mar 6, 2018

// 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")]
Copy link
Member

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.

@ishitatsuyuki ishitatsuyuki 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 Mar 6, 2018
@ishitatsuyuki
Copy link
Contributor Author

r? @alexcrichton

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 setrlimit as it doesn't refresh stack guard configuration.

Additionally, I modified the CI script to the CI runs benefit from this change.

@alexcrichton
Copy link
Member

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?

@ishitatsuyuki
Copy link
Contributor Author

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.

@alexcrichton
Copy link
Member

Ah sorry I'm not too familiar with setrlimit, but what makes it unsafe to extend the main stack?

@ishitatsuyuki
Copy link
Contributor Author

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.)

@alexcrichton
Copy link
Member

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"?

@ishitatsuyuki ishitatsuyuki 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 Mar 8, 2018
@ishitatsuyuki
Copy link
Contributor Author

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:
https://github.com/mozilla/gecko-dev/blob/9bfc0167075b756824a1f6654a5a5cc03bba4f10/js/xpconnect/src/XPCJSContext.cpp

@alexcrichton
Copy link
Member

Looks like CI may be failing?

@ishitatsuyuki
Copy link
Contributor Author

@alexcrichton Fixed.

@alexcrichton
Copy link
Member

@bors: r+

@bors
Copy link
Contributor

bors commented Apr 3, 2018

📌 Commit 7db854b has been approved by alexcrichton

@bors bors 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 Apr 3, 2018
@bors
Copy link
Contributor

bors commented Apr 4, 2018

⌛ Testing commit 7db854b with merge 5758c2d...

bors added a commit that referenced this pull request Apr 4, 2018
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.
@bors
Copy link
Contributor

bors commented Apr 4, 2018

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

@eddyb
Copy link
Member

eddyb commented Apr 12, 2018

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

bors added a commit that referenced this pull request Apr 28, 2018
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
@ishitatsuyuki ishitatsuyuki deleted the unix-no-thread branch July 15, 2018 13:26
@nagisa
Copy link
Member

nagisa commented Jul 25, 2018

There’s one more potential reason to revert this: #52577.

@pnkfelix
Copy link
Member

pnkfelix commented Aug 2, 2018

@ishitatsuyuki would putting this mode under a -Z flag suffice for your purposes?

@ishitatsuyuki
Copy link
Contributor Author

Yes, as it's not really critical.

bors added a commit that referenced this pull request Dec 21, 2018
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.
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.