-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Simplify io::Lazy #58768
Simplify io::Lazy #58768
Conversation
Thanks for this! Looking this over though I think this may allow UB because child threads can still be running when the atexit destructors are running, so returning Other than that though I think this definitely looks like an improvement! |
Thank you, another edge case I didn't think about. So use std::io;
use std::mem;
fn main() {
let throw_away = io::stdout();
mem::forget(throw_away);
print!("Hello, world!");
}
Not sure what to think about this; but soundness is more important than preventing leaks. Ideally we would drop in the |
That may also be thread locals on the main thread which we've historically had a bad time with |
Another thing I don't want to know about 😄. I am not sure I understand the current situation. Or do you mean something more? |
Oh I just mean that the main thread tls dtor leak may be related to the leak you saw above, it doesn't necessarily indicate a bug. I may be misdiagnosing the leak valgrind you pointed out above though |
☔ The latest upstream changes (presumably #58208) made this pull request unmergeable. Please resolve the merge conflicts. |
Ah, now I get it. It does look very similar, but I intentionally caused it with the It took me a bit to understand the issue. Two possible choice are:
I gave the second one a try, it feels slightly cleaner and also just for the fun of it. |
Just realized that my previous attempt did two loads of the atomic: one in the match that guards against |
No worries for the pushes! This version though looks somewhat complicated and possibly more error-prone, so I'm curious if there's some advantages over the previous iteration? |
I am not really happy that it now turns out to be about as complicated or more than before I touched I do think it is an advantage it doesn't lock a mutex on every access but only on initialization, is less creative with raw pointers in a |
Ok no worries! I do mean yeah as just before this PR, and I think this isn't really performance critical per se so while it does indeed look a bit faster, I think for maintenance for now we'll probably want to stick with the previous |
🙁 Just when I was feeling somewhat proud... More serious I think you are right, the complexity is not really worth it. And I learned some things. |
It looks like there's still a constant for |
pub struct Lazy<T> { | ||
// We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly! |
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.
You removed this comment, and the one below about reentrancy -- but it doesn't seem like guard.init()
is ever actually called?
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 comment should be left until we merge #56410 I guess. After that there will not be any init()
method and it would not be UB to call it reentrantly even (it would deadlock instead).
These mutexes are not used reentrantly, so it's fine to not call init()
now.
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.
It is only moved to the top of the file, hopefully the warning is enough there.
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.
These mutexes are not used reentrantly, so it's fine to not call init() now.
This relies on the users of this abstractions -- but the comment saying so has been removed from get
.
It is only moved to the top of the file, hopefully the warning is enough there.
When I added these comments I systematically added them next to the place where the Mutex
is declared. I think this consistency is helpful.
I forgot, why can't this use |
Good question, and I am just not sure. My motivation for avoiding it was because part of what One maybe more valid reason: We do need to have an atomic somewhere that is set with |
I used it only to 'broadcast' the new contents of the |
I think though that this code still has a data race during shutdown? The write of In general this is so non-performance-critical I'd prefer to avoid as much complication as possible and favor dirt-simple code. Especially when we start talking about atomic orderings for code like this I think it's probably overkill, so perhaps the old mutex/cell solution could be used? |
ping . from triage @pitdicker waiting for your reply on the last review |
ping from triage @pitdicker |
io::Lazy
is used inside the standard library by the stdio types as a helper to initialize a global static, and run the destructor at process exit.The previous code took me some effort to read, as it gets creative with raw pointers to use
Box
inside a static, and with0
and1
as special values.It now uses an atomic to set the current state, only acquires a mutex during initialization instead of every time, and no longer requires the data to be wrapped in an
Arc
.There is one minor change: if for example
Stdout
were to be used during process exit, which it isn't, three things can happen:Lazy::get
returnsNone
;Arc
, but not initialize the static. Now it also returnsNone
.But since this case doesn't happen in the standard library, it is not really interesting.
Another change is that
stdin_raw()
,stdout_raw()
andstderr_raw()
no longer return a result. Since that design the code has changed to be able to pick up changes to the stdio file descriptors / handles. The state at initialization is not the reliable end-state it will have for the duration of the process. As allsys::stdio
types always returnOk
, I just removed the needless complexity, includingMaybe::fake
.r? @alexcrichton
I am still working on changing the
Stdout
buffer strategy and detecting a terminal. That is for another PR.