Change:
imp::Thread::sleep(dur) } <ins> /// Used to ensure that `park` and `park_timeout` do not unwind, as that can /// cause undefined behaviour if not handled correctly (see #102398 for context). struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { rtabort!("an irrecoverable error occurred while synchronizing threads") } } </ins> /// Blocks unless or until the current thread's token is made available. /// /// A call to `park` does not guarantee that the thread will remain parked <del> /// forever, and callers should be prepared for this possibility. </del> <ins> /// forever, and callers should be prepared for this possibility. However, /// it is guaranteed that this function will not panic (it may abort the /// process if the implementation encounters some rare errors). </ins> /// /// # park and unpark ///

Discussion:
with synchronization assumes that does not panic. This assumption is however not correct. For instance, a panic occurs if the TLS key used to store the thread info , , , and . The code examples above all use intrusive lists and have to wait on a condition to before destroying the list nodes. If panics, the nodes will be dropped during stack unwinding without checking that condition, leading to undefined behaviour (some of these cases are in , so I'm marking this as a soundness problem with ). Fixing this would either require guarding against panics in each of these cases or ensuring that never panics. label +I-unsound +A-atomic +T-libs +T-libs-api
Thanks for bringing this up! I agree this is a problem. In general, the pattern with parking is: Unwinding would mean leaving that loop even though the condition isn't true. While code right after the loop isn't executed, there are many cases where leaving the current frame before the condition is true is problematic, as all your examples show. I think it's not reasonable to expect every user of to take this subtle pitfall into account. Instead, I believe we should guarantee that never unwinds.
All the situations in which can currently panic are very unusual things that are most likely unrecoverable errors. So I think it'd be fine to just never let unwind, and instead always on panic.
I think Rust should allocate the needed resources eagerly when creating a new thread, to avoid this problem.
It actually does most of the time, as always creates a . However, if the thread is created through other means, the struct needs to be lazily allocated, which can of course fail. Either way, we also need to protect against the case where fails - it should not, but who knows what some obscure system might do (the UNIX parking implementation is used on systems like L4RE and 3DS/Horizon). In general, I think it is easier for maintainability if we just always convert panics to aborts in / rather than going through every system and abstraction to ensure no panics are caused. Still, this is possible to do in the future (as is returning instantly).
I just tripped over an issue in this area. I'm trying to put together a userspace mutex for learning and it works fine apart from when there is a panic.

If there has been a panic, then I get a panic when handling panic and fall over with this stack trace: Note that at 18, some destructors are run, that appear to be releasing Thread local storage. By the time I try to run , at 7, the thread is already invalid (although , I don't know that). I tried: But, I get the same problem. I presume because of raciness between the check for panicking and the panic actually starting.
It looks like you're implementing a global allocator using your thread::park_timeout-based mutex? That's going to be problematic, since the thread parking data is stored in a heap allocation (an ) that gets deallocated on thread destruction.
Thank you. I have an approach to the problem which works as a spinlock, but I was experimenting to see if I could do something more efficient using . It's one of those where I wasn't sure if it was interesting to report, but I thought I'd mention it.