-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
arc::get_mut/Arc::make_unique are racy #24880
Comments
I think you're right. I'm tempted to say this could be fixed by switching from a strong/weak pair to a num_ptrs/num_weak pair, where every pointer (whether strong or weak) is counted in num_ptrs and then weak pointers are also counted in num_weak. This does lower the number of total pointers (strong + weak) that we can have outstanding, but it's not as restrictive as actually dividing a Another alternative might be to use the high bit of the strong pointer as a "there are weak references" marker. I'm not sure how well that would actually work. And of course we could always just use a spinlock instead. |
Using num_ptrs + num_weak shouldn't actually lower the total number of pointers since each |
Upon reflection I suspect that num_ptrs + num_weak fails for the same reason that get_mut fails, because the destructor of |
Although it's possible that using num_ptrs + num_strong instead might possibly work. |
If that works (which sounds plausible) it has the disadvantage of requiring two atomics on clone() (though they're probably the same cacheline, so it's not as horrible as it might be). |
I still haven't thought it entirely all the way through, but I think that num_ptrs + num_strong will work (as long as num_strong is always modified before num_ptrs). As you say, this will require two atomics on clone(). The other alternative that I think would work is using the low bit of the Incidentally, it appears we do not have a spinlock construct in our stdlib. That's rather unfortunate, as ad-hoc simple spinlocks are not generally ideal. Preferably we'd have a construct that spins some number of times (it looks like pthread on OS X defines that number as |
Split the Arc struct in two: Give Arc another marker type parameter that decides statically if the type should support weak pointers or not. Use default type parameters to make Only the NoWeak Arc will permit Default type parameters make this seamless for users that don't need weak pointers. Note: boost shared_ptr has a member function |
@bluss That seems like a very heavy-handed approach. The creator of the As for Boost, I glanced at it yesterday and it didn't appear to me that it supported weak pointers. |
It is heavy-handed, but it is also flexible, and allows users to only pay for what they use. Didn't we already want to make Weak pointers optional? (What other reason are they unstable for?) See boost Smart Pointers doc, it has weak_ptr |
@bluss I'm not really sure why they're unstable, the message just says "Weak pointers may not belong in this module". And you're right, Boost does have So the difference here is our |
I think what we should do today is remove |
@alexcrichton You added |
@kballard I added |
@kvark I would really like to have |
Since weak_count and strong_count are public, Removing unsound unstable API sounds fine. |
@bluss True, you could reimplement the current logic of
Well no, that doesn't make it legal. If you construct a Based on that, I believe that it would be legal to construct this third-party All that said, given that the current scheme of separate atomic |
cc #24564 |
It's a systems language, we don't tell them what to do. We tell them what's possible and where the boundaries lie. We give them unsafe blocks for when they feel they can take over the reins themselves.
Obviously. I didn't say it was a sufficient criterium. Anyway, let's not do this kind of thing where we out-nitpick each other.. All I suggested was if wouldn't it be possible to implement outside of crate. |
The racy functions still exist. We should document that or remove them. |
This is a temporary mitigation for issue rust-lang#24880 which points out that these functions are racy in a particular situation where weak pointers exist. To mitigate this, mark the functions unsafe until this can be fixed or another decision is made.
This is a temporary mitigation for issue rust-lang#24880 which points out that these functions are racy in a particular situation where weak pointers exist. To mitigate this, mark the functions unsafe until this can be fixed or another decision is made. This is a breaking change to unstable API, because the new version requires an `unsafe` block. Review carefully if weak pointers may race for any uses of this API and consider abandoning it. [breaking-change]
Mark Arc function get_mut and method make_unique unsafe This is a temporary mitigation for issue #24880 which points out that these functions are racy in a particular situation where weak pointers exist. To mitigate this, mark the functions unsafe until this can be fixed or another decision is made.
For reference, these functions, together with weak pointers, are used heavily in Servo. I've made a PR to fix this problem more directly: #26610 |
This commit resolves the race condition in the `get_mut` and `make_unique` functions, which arose through interaction with weak pointers. The basic strategy is to "lock" the weak pointer count when trying to establish uniqueness, by reusing the field as a simple spinlock. The overhead for normal use of `Arc` is expected to be minimal -- it will be *none* when only strong pointers are used, and only requires a move from atomic increment to CAS for usage of weak pointers. The commit also removes the `unsafe` and deprecated status of these functions. Closes #24880 r? @alexcrichton cc @metajack @SimonSapin @Ms2ger
Both of these essentially check that
inner().strong == 1 && inner().weak == 1
to check that&mut self
is the only reference to the Arc's data. However in between the checks a Weak pointer could be promoted and the Weak pointer dropped, resulting in both checks succeeding. Adding an additional strong check afterwards won't help as the second strong pointer could then be demoted again.I don't see an obvious way without a two-word atomic read or a generation counter or something of that sort. On 64-bit splitting an atomic64 into two 32-bit sections /might/ be ok, but on 32-bit only allowing 16 bits of refcount seems bad. (There might be something with using a high bit as a lock bit or such?)
The text was updated successfully, but these errors were encountered: