-
Notifications
You must be signed in to change notification settings - Fork 85
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
launcher: replace parent process on supported platforms. #569
Conversation
This seems to work great. With NVIDIA's profilers it sadly still requires a
... even though we can break on the segfault handling by using
This isn't a multiprocess issue, as there's only one:
@Keno |
Yes
Not really. If you're using BugReporting or whatever, we could somehow mark which process is the interesting one in the trace, but for vanilla rr, there isn't really a way to know. After all, maybe you want to debug the launcher. |
FWIW, BugReporting calls |
Bump, @davidanthoff. |
Wholeheartedly endorse this, I use juliaup everywhere now but every so often I need to use GDB and get confused by the fact that my breakpoints are not working |
While https://docs.rs/nix/0.26.2/nix/unistd/fn.fork.html#safety is not super clear, it has this sentence
which to me suggests that we can't do things like update the version db in the child process? The example just above that section also says that one can't call things like The way I am reading that section, it is not enough to not introduce multi-threading ourselves into |
Only if the parent process is multithreaded. AFAIU, the example would be safe to use |
No, it does not. |
But if say any library we used were to spawn a thread, then we would be in trouble, right? Say our https request library, or anything else? We would essentially have to audit all of those packages? And if we wanted for example to adopt something like I'm wondering whether another strategy would be to make this replace thing opt-in (either via a command line flag, an env var or a config file setting), and when that is the case, we just skip the entire background "check for new versions" step. We would then not have to fork at all, but could just do the replace step by itself? |
Only if those threads were spawned before the fork. Not doing the fork would be fine by me too, but I'd rather we use some heuristic to detect gdb/rr/nsight/etc instead of using opt-in flags/env vars/settings. |
Yes, that sounds good to me as well! |
IMO, the spirit of this PR is that the fork happens very early on; before we've started to do significant work. Forking is not a rare thing to do in the linux world, so most rust crates "should" avoid spinning up threads right at startup, I would imagine. Verifying this is easy, I launched |
This would also likely resolve some issues, such as #607 :-) I am confused about the concerns regarding multithreading and async IO here, but that's probably because I am completely unfamiliar with how If there are concerns about some library being badly designed and violating this rule by creating threads "too early" then I'd suggest to simplify the launcher to just this:
Such a launcher would have minimal (ideally: no) external dependencies and therefore should be easy to audit against the problem of multi threading being introduced "accidentally". The two subprocesses of course can use multithreading arbitrarily. Looking at https://github.com/JuliaLang/juliaup/blob/main/src/bin/julialauncher.rs it seems that a DB update already is handled by launching a separate process, so this all seems not far from what I described above. So I am somewhat confused as to what the major concern here is? |
Bump. Whats the status here? Anything except a conflict resolve needed before merging? |
I have another question about this: does this work across 32-64 bit boundaries? I.e. say Juliaup is 64 bit (so |
This simplifies use of juliaup with, e.g., debuggers. Database updates are executed in a forked process.
@davidanthoff that should work. Otherwise a 64 bit bash/zsh/... could not launch 32bit binaries and vice-versa (they all do |
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.
Thank you, this looks very good, with the caveat that I don't really speak rust -- but I can follow the general control flow and this looks like what I would expect.
So I hope this can be merged soon and released :-)
I couldn't reproduce this; and I don't see any threads/processses being created ( |
Rebased, addressed comments, and made sure there's no new GH:A warnings. I also did some extensive testing locally, and this seems to work fine. I didn't test Windows, but nothing changed there (as is obvious when inspecting the diff with whitespace changes removed). So this seems good to go? |
I'll merge this in a couple of days assuming no news here. |
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.
Has someone tested all the various background task code paths for this? Things like a) initial setup and b) an actual version db update (i.e. where the version db on the server has actually changed)? I think the self-update is something we'll only be able to test once we put this out to at least the dev channel, but we should try that right after this is merged as well. I think a) is partially covered by tests, but b) probably not.
And I think this here still means that we essentially rule out that we ever adopt something like tokio with threads down the road? Probably not even in juliaup
, as there is a fair bit of code shared between the binaries...
Not it does not mean that, as I explained above. |
AFAICT initial set-up isn't affected by this change. And the background tasks still work; I don't see why they would not, the forked child behaves identical to the parent.
|
Also, the double fork+exec for the update process is probable better anyway since that's required to avoid a zombie child and is the standard way to start a daemon process on UNIX, which presumably is what we want the updater to be. Overall, this is just the correct design on UNIX systems. It cannot be done on Windows, of course, but better to do this correctly where we can. |
The Zombie problem doesn't happen on windows, so it doesn't need the daemonize workaround |
This simplifies use of juliaup with, e.g., debuggers. Database updates are executed in a forked process.
There's a couple of tricky aspects:
fork
; essentiallyjulialauncher
may not become multithreaded for this to workbecause we replace the parent process without waiting for the child, there'll be a zombiejulialauncher
process until the parent terminatesI don't think these are dealbreakers, but it's probably worth discussing.
Untested on Windows as cross-compilation doesn't work for
juliaup
(due to thebuild.rs
).I'm also a Rust newbie so the code probably can be improved.
cc @vchuravy @staticfloat
Fixes #552, fixes #607, fixes #645