notice how the destructor of DropGuard is never called
Yet the DropGuard variable is deallocated
We do guarantee that destructors of variables are called before they are deallocated (required for Pin being sound)
@Amanieu I recall an issue about this somewhere but I can't find it
I also recall that actually something like this should cause the other thread to unwind from a cancellation point
Good thing it's unsafe then ;)
It sometimes unwinds, and sometimes just gets deallocated without unwinding
it depends on whether I run it in debug or release
and the timings
So I kind of expected that this would unwind
That's bad in another whole dimension (all the syscalls or libc APIs are
But what I did not expect is that this would also terminate the thread without unwinding
I'm not sure how that could even happen, a signal maybe ?
But I don't think
std::threads catch any signals by default
@gnzlbg The difference between debug and release is probably because the nounwind marking on the FFI sleep call is wrong.
I don't think we misoptimize this case due to
nounwind, but I can check that
(the only known misoptimization requires
If LLVM can see that there is no way for the unwind path to be reached then it will simply eliminate the unwind path.
Unwinding is still happening, it's just that the destructor isn't getting called because LLVM "knows" that it is unreachable.
One possible fix is to open up "man 7 pthreads" and add
#[unwind] to all functions listed as cancellation points.
@Amanieu but the issue is that in debug builds destructors don't run, but they do run on release
that's the opposite of what I would expect if your hypothesis was true (LLVM optimizing debug builds, but not release builds)
In your second example you have a race. Add handle.join() at the end.
The threads are overlapping
Ah, I see what you mean now
@Amanieu so the race is part of the problem
.join(), the thread gets dettached
pthread_cancel was already called
so even if unwinding starts happening, it can happen that
main exits before that
If you look up, it's aborting with an error
FATAL: exception not rethrown
In the other case I mean
In that case, without
.join(), the second thread has already been cancelled before main tries to exit, but it maybe gets dettached before the unwind is raised there
Oh that's just main exiting before the thread gets to handle the exception
So this is all a false alert, unwind is working fine.
it is still unsound, but it is working reliably
Well apart from FATAL: exception not rethrown, but that's because it's crashing in the
catch_unwind at the root of the thread
The unwind exception thrown by pthread_cancel is not meant to be caught
Yes, the problem is that
catch_unwind probably doesn't know how to handle foreign exceptions
or the force exception
Maybe we should fix
catch_unwind to ignore any non-Rust exceptions
that's what C++ does
well, C++ allows you to catch them in
but not in any other context
so if the error is "exception not rethrown", this must be a "force unwind"
and the Itanium ABI says that those must always be rethrown
"forced" unwinding (such as caused by longjmp or thread termination).
During "forced unwinding", on the other hand, an external agent is driving the unwinding. For instance, this can be the longjmp routine. This external agent, not each personality routine, knows when to stop unwinding. The fact that a personality routine is not given a choice about whether unwinding will proceed is indicated by the _UA_FORCE_UNWIND flag.
IMO catch_unwind should only catch native rust exceptions. If you want to catch foreign exceptions, write a wrapper in C or C++.
but this is not a foreign exception
this is a ForceUnwind
The ABI allows catching foreign exceptions and interrupting unwinding. What it doesn't allow is catching ForceUnwinds and interrupting unwinding (you can pause it, do something, but must rethrow the ForceUnwind afterwards).
That doesn't mean that
catch_unwind should catch foreign exceptions
But even if it doesn't, we probably want to do so on the thread boundary anyways, and
or something like that
Otherwise the unwind will just terminate the process anyways IIUC
Or how is unwinding propagated across a thread boundary ?
Ah! This particular unwinding is allowed to reach the thread boundary, and that will cancel the thread
what happens with -C panic=abort?
moved that to the UCG repo: https://github.com/rust-lang/unsafe-code-guidelines/issues/211
(we don't already have an issue covering that, do we?)
but we dont have a fitting
A-* I think... hm
@comex AFAICT, with
-C panic=abort, the stack frame unwinds
Otherwise you can't cancel threads in C without
Destructors aren't necessarily run when that happens though