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

feat: Use channels to maintain job tokens & reuse the implicit token without dropping it first #878

Merged
merged 12 commits into from
Oct 19, 2023

Conversation

osiewicz
Copy link
Contributor

@osiewicz osiewicz commented Sep 30, 2023

This is a follow-up to #779 and addressing #858. This approach uses an additional thread for polling the jobserver.
Per my local tests, it fixes #858

@osiewicz
Copy link
Contributor Author

osiewicz commented Sep 30, 2023

I am wondering if we could also attempt removal of the current wait_thread in this code, as per #829 it is there mostly to avoid a deadlock with acquire - which happens in yet another separate thread anyways as of this PR.

src/job_token.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/job_token.rs Show resolved Hide resolved
@NobodyXu
Copy link
Collaborator

NobodyXu commented Sep 30, 2023

I am wondering if we could also attempt removal of the current wait_thread in this code, as per #829 it is there mostly to avoid a deadlock with acquire - which happens in yet another separate thread anyways as of this PR.

JobTokenServer::acquire from this PR still blocks, so it would still be required.
However I don't think it's necessary for JobTokenServer to spawn a help thread, that's definitely not necessary.

If the help thread can be removed, then you can also replace that mpsc channel with an Arc<AtomicBool>.

@osiewicz
Copy link
Contributor Author

osiewicz commented Oct 1, 2023

Eh, I've flirted with the idea of Arc<AtomicBool>, though I like a channel approach a bit more, as it makes obtaining a token opaque - you don't care whether the token is owned by the current process or if it was obtained from the jobserver. It allows for token reuse and to obtain an implicit token the same way a jobserver token is obtained without having to release it.
I agree though that the additional thread is probably not necessary.

@osiewicz
Copy link
Contributor Author

osiewicz commented Oct 1, 2023

Eh, I've tried to get rid of helperthread, leading to a deadlock of a build on one of my test projects. I believe the issue here is similar to what you've ran into in #779 (comment) - we can't just acquire, as that might lead to the deadlock. By having a separate thread and a channel we can request a token like we do with acquire - separate thread sounds redundant, right? Well, if acquire is never going to return, then a separate thread is blocked, not the main one - and we can grab the implicit token once that becomes available.
Attaching a patch on top of 52afaf1, perhaps I've done something incorrectly.
no_helperthread.patch

src/job_token.rs Outdated Show resolved Hide resolved
@NobodyXu
Copy link
Collaborator

NobodyXu commented Oct 1, 2023

Well, if acquire is never going to return, then a separate thread is blocked, not the main one - and we can grab the implicit token once that becomes available.

I see why it doesn't work: Setting new values on Arc<AtomicBool> cannot stop jobserver::Client from blocking.

However in the current PR it calls mpsc recv, which will succeed and stop blocking when the implicit token is released.

(It's a shame there isn't built-in async executor in stdlib, otherwise this would be much, much easier.)

@NobodyXu
Copy link
Collaborator

NobodyXu commented Oct 1, 2023

Regarding the wait_thread, it can be removed by providing a non-blocking try_acquire using mpsc::Receiver::try_receive.

Since the spawning thread can consume all tokens (implicit and explicit), wait_thread is necessary unless we add try_acquire, and switch to waiting on spawned children on failure.

However this sounds so complex that I believe it should be a separate PR.

@NobodyXu
Copy link
Collaborator

NobodyXu commented Oct 1, 2023

Regading jobserver, I think that maybe it's a good idea to have a vendored implementation that never blocks.

cc only needs to:

  • open a jobserver
  • create a jobserver
  • try acquire a token
  • release a token

src/job_token.rs Outdated Show resolved Hide resolved
src/job_token.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@NobodyXu NobodyXu left a comment

Choose a reason for hiding this comment

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

LGTM, but you would need a team member's approval to merge

@thomcc
Copy link
Member

thomcc commented Oct 3, 2023

I'll take a look later. I suspect it's mostly fine with @NobodyXu's approval.

@NobodyXu NobodyXu merged commit 4f9866e into rust-lang:main Oct 19, 2023
16 checks passed
@NobodyXu
Copy link
Collaborator

Thank you @osiewicz !

P.S. I was just granted the permission to approve and merge PRs in this repository.

@osiewicz
Copy link
Contributor Author

Thanks!

@NobodyXu
Copy link
Collaborator

I just discovered a problem with this PR: The mpsc in the jobserver will never be dropped so the token will never be put back to jobserver.

I'm already working on a new PR which dramatically improves the parallel compilation and it will be in ready in a few days.

@osiewicz
Copy link
Contributor Author

osiewicz commented Oct 22, 2023

Uh oh, that's no good.
Off the top of my head though, does the implicit token need to be returned? We never explicitly request it so it's effectively managed by a caller of cc crate. For the acquired tokens, we should be returning them to the pool by the impl of JobToken for which .forget was called.

That's just off the top of my head though and I'm writing that reply at 3AM, so you know, I am probably wrong in some of my assumptions. :x

@NobodyXu
Copy link
Collaborator

Off the top of my head though, does the implicit token need to be returned?

No

For the acquired tokens, we should be returning them to the pool by the impl of JobToken for which .forget was called.

But for tokens which are still in the mpsc, they will never be returned to the jobserver.

@NobodyXu
Copy link
Collaborator

I've opened #889 which fixes this and completely removes usage of thread, please have a look/review if you have time.

osiewicz added a commit to zed-industries/zed that referenced this pull request Nov 12, 2023
This resolves a minor issue where build scripts could've acquired more job server tokens from Cargo than allowed by `-j` parameter. I've filled a PR at rust-lang/cc-rs#878 and we've iterated on the design over there since.

TL;DR: some build scripts may complete a tad bit quicker, potentially shaving off a few seconds off of debug/release builds.
osiewicz added a commit to zed-industries/zed that referenced this pull request Nov 13, 2023
This resolves a minor issue where build scripts could've acquired more
job server tokens from Cargo than allowed by `-j` parameter. I've filled
a PR at rust-lang/cc-rs#878 and we've iterated
on the design over there since.

TL;DR: some build scripts may complete a tad bit quicker, potentially
shaving off a few seconds off of debug/release builds. Full description
of the issue is available in
rust-lang/cc-rs#858

Release Notes:

- N/A
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.

Issues with releasing and reacquiring implicit jobserver token
3 participants