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

Disable thread support in libtest if RUST_TEST_THREADS=1 #107396

Closed
wants to merge 1 commit into from

Conversation

ianks
Copy link

@ianks ianks commented Jan 28, 2023

After #103681, each test now runs in it's own thread if the platform supports it. This was true even if RUST_TEST_THREADS=1. Unfortunately, this causes some subtle breakage for certain libraries that may rely on thread-local storage being shared across tests (as is the case for libruby and Julia, and probably more).

The feature never intended to cause any breakage, so restoring the previous behavior should be OK. I considered adding a new option, such as RUST_TEST_THREADS=0, which would allow both the new and previous behavior to exist side-by-side. But the idea of 0 threads felt... wrong. So I think this is the best way forward.

This is my first rust PR, so please let me know if I missed anything! ❤️

@rustbot
Copy link
Collaborator

rustbot commented Jan 28, 2023

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Mark-Simulacrum (or someone else) soon.

Please see the contribution instructions for more information.

@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 Jan 28, 2023
@rustbot
Copy link
Collaborator

rustbot commented Jan 28, 2023

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@Mark-Simulacrum
Copy link
Member

r? @thomcc

It feels like we might want this to be a separate knob, not related to threads=1...

@rustbot rustbot assigned thomcc and unassigned Mark-Simulacrum Jan 29, 2023
@the8472
Copy link
Member

the8472 commented Jan 30, 2023

It feels like we might want this to be a separate knob, not related to threads=1...

Agreed. Sometimes testing with N > 1 threads could also benefit from reusing threads instead of running a thread-per-test, e.g. when loads of thread-local caches or similar things need to be initialized.
I don't think we ever had thread pooling in libtest, but choosing a generic option name would allow it to cover both cases (all tests in 1 thread and all tests on pooled threads).

@thomcc
Copy link
Member

thomcc commented Jan 30, 2023

This kinda feels like a libs-api decision, regarding how this "should" work.

@thomcc thomcc added I-libs-api-nominated Nominated for discussion during a libs-api team meeting. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Feb 13, 2023
@thomcc
Copy link
Member

thomcc commented Feb 13, 2023

Actually, I've realized that promising this and doing it in this manner would lock us into never fixing some bugs in libtest (like having no slow test notification on machines with 1 core). Let me dig up some old code and make a counter-proposal.

@thomcc thomcc removed the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Feb 13, 2023
@uazu
Copy link

uazu commented Mar 27, 2023

As a vote in favour of the intent of this change (although I'm not following the details):

I have a case where I need to test code that effectively locks future invocations of that same code to the same thread. This is to make internal use of global variables by the code sound. This is for an optional feature-based optimisation in the crate, but the optimisation is worthwhile for the cases where it is used, saving both memory and CPU. It tested fine until 1.67, but since then I can't test it because it immediately panics when it notices that it's running on another thread.

Making RUST_TEST_THREADS=1 use just a single thread again would fix my problem. Right now I have to use a hacky workaround, using --list to get a list of tests and running them one by one.

@bors
Copy link
Contributor

bors commented Jul 1, 2023

☔ The latest upstream changes (presumably #111992) made this pull request unmergeable. Please resolve the merge conflicts.

@knopp
Copy link

knopp commented Jul 27, 2023

Rust default test harness is very much unusable for anything on macOS that requires execution on main thread. Unfortunately main thread is special (so that on some places there are actually checks for pthread_main_np, i.e. in libdispatch. It would be immensely helpful if there was way to run tests with the default harness directly on main thread.

@RalfJung
Copy link
Member

The feature never intended to cause any breakage, so restoring the previous behavior should be OK.

FWIW running it all in one thread was also never intended to be a feature -- previous behavior (Rust 1.0 - ~1.30) was to always spawn threads; #56243 did the "run in a single thread" thing as a hack to make cargo miri test work.

So, neither #56243 nor #103681 was intended to be more than an internal refactor, and neither should be read as indicating intent. Forcing a single thread has disadvantages (e.g. #59122).

@tiagolobocastro
Copy link

Something like this would be useful for us, as we found out yesterday during rust update.
We have an extern dependency which allows 1-time init for the process lifetime and we have to use the same thread everytime we interact with it.. so not much we can do from our side.

However seems other things also get broken by the previous behaviour which is a no-win situation.
I would suggest reverting back to previous behaviour and try to come up with an approach that would work better overall, maybe exposing a separate env variable or perhaps a threads flavour to the test macro?

#[test(flavour = "static_thread")]
fn test_xyz() {}

@the8472
Copy link
Member

the8472 commented Aug 23, 2023

If the library has a requirement to run on a specific thread then it should do what many UI libraries do and provide something like fn dispatch_to_ui_thread<R, F>(f: F) -> R where F: FnOnce() -> R + Send, R: Send that submits it via a channel it to the initialized thread and waits for the result to be returned.

Then tests that interact with that library can serialize themselves by dispatching to that thread. Other tests that don't touch that part can still run in parallel.

None of that requires any changes to libtest. This is a problem of libraries that have additional requirements but do not provide the necessary facilities to meet those requirements in their API.

@bjorn3
Copy link
Member

bjorn3 commented Aug 23, 2023

None of that requires any changes to libtest.

It does require a change to free up the main thread for use by the ui framework. In many cases the ui thread can only be the main thread, but it is currently being used by libtest so nobody else can run anything on it.

@thomcc
Copy link
Member

thomcc commented Aug 23, 2023

Yeah, apple provides those functions but a function you sent to that queue would never get run (and if you blocked on it, you'd deadlock), because libtest doesn't start the main loop.

Overall I think this is something that should be discussed by t-testing.

@knopp
Copy link

knopp commented Aug 23, 2023

This is a problem of libraries that have additional requirements but do not provide the necessary facilities to meet those requirements in their API.

The API is all there - I can easily dispatch things to main thead on macOS (i.e. dispatch_async or CFRunLoopAddSource) but that will not help because the main thread is stuck in libtest instead of running the run loop.

@the8472
Copy link
Member

the8472 commented Aug 23, 2023

It does require a change to free up the main thread for use by the ui framework.

Last time (it has been a few years) I had to deal with these kinds of issues the framework had a way to initialize the UI event loop on something that was not the program's first/main thread.

The API is all there - I can easily dispatch things to main thead on macOS (i.e. dispatch_async or CFRunLoopAddSource) but that will not help because the main thread is stuck in libtest instead of running the run loop.

Ok, I haven't dealt with macos. Does it not have that?

@knopp
Copy link

knopp commented Aug 23, 2023

Ok, I haven't dealt with macos. Does it not have that?

Unfortunately no. There are hard-coded assumptions about main thread in various places.

https://github.com/opensource-apple/CF/blob/3cc41a76b1491f50813e28a4ec09954ffa359e6f/CFRunLoop.c#L1482-L1487

CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

There are similar checks all over AppKit, libdispatch, etc. too. The only way for things to work properly is to keep running the run-loop (CFRunLoopRun()) on main thread (first created thread - for which pthread_main_thread_np() returns 1).

@workingjubilee workingjubilee added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-testing-devex Relevant to the testing devex team (testing DX), which will review and decide on the PR/issue. labels Oct 6, 2023
@workingjubilee workingjubilee removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 6, 2023
@Dylan-DPC Dylan-DPC removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 15, 2023
@thomcc
Copy link
Member

thomcc commented Feb 1, 2024

I'm going to be away for a few months, so I'm rerolling my PRs so that folks don't have to wait for me. Sorry/thanks.

r? libs

@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Feb 1, 2024
@rustbot rustbot assigned m-ou-se and unassigned thomcc Feb 1, 2024
@m-ou-se m-ou-se removed the T-testing-devex Relevant to the testing devex team (testing DX), which will review and decide on the PR/issue. label Feb 21, 2024
@m-ou-se
Copy link
Member

m-ou-se commented Feb 21, 2024

I don't think we should make this behaviour implicit on RUST_TEST_THREADS=1. We might want another environment variable or a specical attribute to run a test on the main thread for example, but that's a design question separate from this PR, that will require an ACP and should probably be designed together with the testing team.

@rfcbot close

@rfcbot
Copy link

rfcbot commented Feb 21, 2024

Team member @m-ou-se has proposed to close this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. labels Feb 21, 2024
@m-ou-se m-ou-se added the T-testing-devex Relevant to the testing devex team (testing DX), which will review and decide on the PR/issue. label Feb 21, 2024
@m-ou-se
Copy link
Member

m-ou-se commented Feb 21, 2024

cc @rust-lang/testing-devex

@epage
Copy link
Contributor

epage commented Feb 21, 2024

I agree with the disposition to close based on what I saw in the thread.

Another workaround for this is to use a custom test harness (libtest-mimic can help). Of course, I'd love to improve the experience of using a custom test harness. If someone would like to help out in that effort, come on over to https://github.com/rust-lang/libtest-next

@ianks ianks closed this Feb 22, 2024
@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. labels Feb 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-libs Relevant to the library team, which will review and decide on the PR/issue. T-testing-devex Relevant to the testing devex team (testing DX), which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.