-
-
Notifications
You must be signed in to change notification settings - Fork 925
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
Instance variable reads need a read fence to be fully volatile #3353
Comments
This is in prep for officially saying we don't guarantee that ivarrs will be volatile. Users concerned about this are recommended to look into concurrent-ruby, which provides better guarantees and utilities for volatility, atomicity, and more. Note that there may still be a chance that other volatile accesses around the variable updates force it to be effectively volatile, but this will not be the case when reifying variables to be Java fields, as we hope to do in an upcoming release. See discussions surrounding ruby-concurrency/concurrent-ruby#422 for more details. See also #3353 for discussion about get volatility prior to this upcoming change in policy.
After examining this a bit, I'm not sure we actually need to do anything here. The concern for concurrency would be thread A writing at the same time thread B is reading, correct? In thread A, there's two situations:
@pitr-ch @jerrydantonio Does this sound right? BTW: see the commit I'll push shortly for preparations to remove our explicit varTable entry update's memory fence in an upcoming release. |
It is not:
The code in |
I think this does not require any action. But since reads of ivars do not have any volatile reads nor there protected otherwise they are allowed to see up-to-date or any older value. The write part and grow behave as atomic and as volatile in respect to each other which prevents lost ivar updates or lost of newly defined ivars. The read does not see it as atomic operations but the way how it's implemented it will not break, it might just read values from old ivar array until it sees the new var table, etc. I think that's ok though. |
@pitr-ch I agree. Given that we have never really been properly volatile for ivar reads and want to officially guide folks to use other explicit mechanisms (such as those in concurrent-ruby or hopefully in Ruby 2.4) I'm going to close this as Won't Fix. |
Yes, this is currently a But, in the future, if JRuby were to provide a volatile i-var implementation, this would need to fixed. I'm thinking I was too generous of the worst case scenario. I still haven't tested this, but for example, if JRuby was running on Graal, I would expect it to aggressively "cache"/canonicalize plain Java field/array reads. A code like this, would be "concurrency-broken": while @volatile_flag
# wait/busy spin
end
# do something! I don't know about C2, but, from my experience, Graal would aggressively constant fold through |
@thedarkone It also happens on C2, at least with Java: https://gist.github.com/eregon/23cb7e18fe23fdfbb1c4 (The loop OSR and the read is only done once when compiled). |
@eregon Your case is somewhat simpler than the JRuby (non-Truffle) ivar case in that it doesn't involve an array access. Array accesses, since they need a bounds check, probably break a lot of this folding in C2. Under JRuby, where the array access is also guarded behind type checks, and under non-indy execution additionally wrapped inside code that inlines but can't be profiled to eliminate bounds checking...it's even less likely to happen. If we do provide a volatile ivar, it will be via an explicit declaration, something like this: attr_accessor :foo, volatile: true Which will make both accessors and direct accesses act like a volatile field. Those plans are under way now, and I'll try to submit a feature request to MRI soon (since all they need to do is no-op, due to the GIL). |
To avoid confusion, in my post above - with "on Graal" I meant, normal (non-truffle) JRuby run with Graal as a JIT compiler. @eregon thanks for the confirmation, I wasn't sure about C2, because, in general, I think they sometimes choose to err on the safe side (because of the prevalence of the un-thread-safe code), ie the same way they don't trust @headius: since @eregon is trying to shame me by actually writing some code 😛, it seems that arr access makes no difference :)
I guess hooray for C2?
You already have https://bugs.ruby-lang.org/issues/11539 (and an older https://bugs.ruby-lang.org/issues/8259). Is something new coming up? |
Indy access, if it inlines all the way through, would also include an accessing and comparison of the target object's metaClass field versus a cached value. This is a non-volatile field on all Ruby objects, accessed via a virtual call. Non-indy access generally won't inline (except in the most trivial cases) because it shares some code across all ivar access sites. Also keep in mind that the actual truth test is a virtual call to I suppose it's possible these would all fold away. It seems unlikely, except in the most trivial cases like if there's just one object in flight and it's truthy.
I'll be adding an MRI patch to the former issue, to move it along. |
@headius, @thedarkone I've been preparing a proposals for documenting Ruby memory model, volatile ivars, and final ivars, making it all work together. I'll be submitting it in few days. |
We are moving toward making instance vars not be always volatile, but until then our current logic is asymmetrical: we only have a fence for writes.
We need to add logic into StampedVariableAccessor to do a readFence on reads (and probably a sync in the slow SynchronizedVariableAccessor) as well.
The text was updated successfully, but these errors were encountered: