-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Sync
should imply RefUnwindSafe
- lots of missing impls
#54768
Comments
Note that some of these implementations (eg. the one on These types incorrectly (but not unsoundly) implement This is very similar to issue #41622 I think a better fix for the |
Another side effect of this is that any time you have a This contributes a lot to the usability issues with |
cc @RalfJung |
Incidentally, that would also have prevented #41622. So yes please! Don't forget the guards of However, otherwise I am not sure what to add here. @Centril do you have any concrete question? As mentioned above, That said... some things strike me as odd: impl<'a, T: ?Sized> !UnwindSafe for &'a mut T If Why is |
I don't think that follows - things with internal mutability can be The implications are:
I don't think this says anything about non-reference types implementing
This is where the "logic" is a bit fuzzy, but I think it's based around expectation: you should expect that you might see intermediate results in an atomic type (since they are usually modified by another thread) whereas when dealing with a single thread, it is more surprising to see your invariants violated (why For this reason, a mutable reference to an atomic is not unwind safe, because code may change it to point to a completely different atomic variable (temporarily) and not expect the calling code to be able to observe that. |
@RalfJung I have no questions; just thought you should take a look at it as it is your area of expertise :) |
Thinking about this a bit more, I think the documentation could use some work too: the documentation indicates this is about poisoning (ie. This would mean that libraries like The only way to have The poisoning feature provided by the standard library types is then just an additional safety net those types provide: related to, but separate from By that logic, the set of types which should not be
|
Wait, |
Sorry, that should have read |
That's true, so |
@RalfJung except that |
Well, yeah. But this then goes back to "what does |
Make sure that the StatsdClient is unwind (panic) safe by ensuring that pointers to sinks and error handlers require the object to be unwind safe. Make the QueuingMetricSink unwind safe by not using a CondVar and Mutex but instead using an AtomicBool to indicate when the worker is stopped. Additionally, assert that the Crossbeam MsQueue is unwind safe because it implements Sync. See rust-lang/rust#54768 Fixes #77
Make sure that the StatsdClient is unwind (panic) safe by ensuring that pointers to sinks and error handlers require the object to be unwind safe. Make the QueuingMetricSink unwind safe by not using a CondVar and Mutex but instead using an AtomicBool to indicate when the worker is stopped. Additionally, assert that the Crossbeam MsQueue is unwind safe because it implements Sync. See rust-lang/rust#54768 Fixes #77
tries to read, fails to read why's Cell not RefUnwindSafe? it's just like a Mutex but better - Cell works by-move or by-copy, whereas Mutex posions on panic. the by-move or by-copy semantics are roughly equivalent because you can't be in the middle of a computation while also being inside a &Cell. Mutex: posions because you can be in the middle of a computation while also being inside the Mutex. this could lead to memory unsafety (altho I can't currently come up with any way to cause memory unsafety using e.g. Vec's methods, and if there was such a way I think it'd be considered unsound anyway) if the poisoned lock is accessed. Cell: requires you to move the object before attempting to mutate it. at that point, an unwind would cause that object to be dropped, thus preventing it from being observed by the Cell. Cell doesn't need poisoning because it makes stronger guarantees than poisoning. or am I wrong? |
@SoniEx2 poisoning/unwind safety are not memory safety concerns - accessing a poisoned mutex cannot cause memory unsafety (and there is a safe API to do this). Unwind safety is an artificial distinction introduced to separate single-threaded types with interior mutability into two categories:
What is "valid" is entirely up to the user of the type to decide, and also this distinction makes no sense for multi-threaded (Sync) types (if they had two states, they could switch between them at any moment in another thread, effectively making it a single state from the point of view of someone accessing it) Cell being On the other hand, with an For multi-threaded types, you always have to leave it in a "valid" state, otherwise accessing any of them might be a logic error if another thread happens to panic: it's just that one of those "valid" states may now be "poisoned", if a multi-threaded type chooses to implement poisoning (but it will still be |
You can't mutate a Cell, except through replacement, unsafe pointers, or &mut Cell. Replacement doesn't panic. The panics cannot happen while it's being replaced. So you guarantee a Cell won't have an "invalid" state because any "invalid" state happens outside the Cell. So Cell should be RefUnwindSafe? Note that I'm explicitly not talking about RefCell or UnsafeCell. |
The guarantees here have the little to do with the type itself - they are guarantees made by the user of the type to the user of the type. In the case of |
And I'm not talking about UnwindSafe either, but about RefUnwindSafe... |
|
&mut T isn't UnwindSafe. Rc is. Mutex is. RefUnwindSafe indicates that being able to mutate a type is unwind-unsafe, but having access to it through & isn't. I think? Cell should be RefUnwindSafe just like the Atomic*s, because they behave about the same. |
After a long discussion with @Diggsey on reddit, I have to disagree with the basic premise of this issue.
Both are unrelated and fully orthogonal except: You can shoot yourself in the foot by having a There is no relation between threading and unwind safety other than that rust typically will only unwinds one thread when a panic happens and thus the thread boundary is a panic boundary. That makes one way out of several (not covered by warning signs) to run into unwind safety issues. Using
Note that this is but one way. Destructors observing data also allow you to run into trouble without warning signs. TLS and globals also allow you to run into trouble without warning signs. In terms of logic reasoning, this proposal is not much different than proposing that we should auto implement Or it might be argued that we should have In no way can be sensibly concluded that something is The fundamantal problem I see with considering It's also been pointed out that the stablization RFC does state the intention was implement As for actionable items here, I think we can indeed implement |
Sync
is a strictly stronger requirement on a type thanRefUnwindSafe
, because any type which can be accessed concurrently by multiple threads, can also be accessed after one of those threads panics.However, if you compare the list of implementations for
Sync
:https://doc.rust-lang.org/std/marker/trait.Sync.html#implementors
And those for
RefUnwindSafe
:https://doc.rust-lang.org/std/panic/trait.RefUnwindSafe.html#implementors
There are clearly a lot missing.
Here's my best guess at some missing implementations:
It's likely I've missed some where
Sync
is automatically implemented because of an explicitSync
implementation on a private type which has impacted automatic trait implementation. (Condvar was one of these)The text was updated successfully, but these errors were encountered: