Code change:
I tried this code through miri: I expected to see this happen: No observable behaviour, including from miri (aside from "Unsupported Operation" errors). Instead, this happened: reports undefined behaviour in "Destroying locked mutex" when calling (Note: this report is correct, calling on a locked mutex is prescribed to be undefined behaviour by POSIX) This was tested on all latest versions of rustc, all using miri 0.1.54, on <details<summaryMiri Backtrace</summary<p</p</detailsThis is caused by issue presented in . However, I believe that it deserves new attention given that it causes miri to fail in safe code (and is not a miri false positive, as miri is correctly reporting undefined behaviour in calling ).
Assigning a priority according to the and removing . modify labels +P-high -I-prioritize
Well, Miri is just a tool to detect issues like this. It looks like back then the decision was made to ignore what POSIX says and instead to rely on what certain implementations do. Since Miri checks POSIX compliance as strictly as it can, this leads to Miri errors in safe code. Ideally we'd work towards getting the spec changed (there seems to be no good reason for this UB, and without something like Rust it is unclear why anyone in reasonable code would even want to do this). That's probably very hard though. Are there work-arounds where we can avoid calling when the lock is still held? Seems hard to do that atomically...
Indeed. And, as I've mentioned in the thread, I wouldn't consider this a bug in miri, for precisely that reason. One half of it could be destroying it potentially concurrent to it becoming locked, since destruction is presumably not atomic, though this wouldn't be an issue for rust. Another reasonable possibility would be for enhanced implementations that, for example, check and trap deadlocks, but as a consequence, couldn't permit being destroyed while locked (the issue was discussed somewhat on the Rust Community Discord, in the #black-magic channel, and this was one potential reason for the POSIX UB). I've actually considered this previously for my implementation, and that's really the only option to avoid strict UB, without fixing the spec (which will take some time, though I don't know how much). Since the destructor is &mut self, it doesn't need to be atomic. It could just be an AtomicBool (or any other atomic, possibly AtomicPtr, which IIRC is the only atomic dependency in libstd) that gets set on being locked, and cleared on unlock.
We could , and if we successfully acquire the lock, do and . If we couldn't acquire the lock, just leak...
I was about to ask what if someone else acquires the lock again just after we ... but that cannot happen since we have here. So, yeah, that could work.
Posix says it's UB to call that function on a different thread than the one that locked the mutex, so that wouldn't work, unfortunately.
I assume that was in the case that successfully called , so it's being unlocked on the thread that locked it.
Oh, right. I misread the comment. :) Then the only question that remains is whether it's okay to not call but still drop/reuse the memory. Without calling destroy, pthread doesn't know that memory is no longer a mutex it is managing. If it has some kind of linked list of mutexes or something, that could result in UB.
It probably has to be properly leaked. I would assume that the raw mutex needs to be properly treated as ned.
We can leak the box or drop the box depending on whether it's UB or not.
We have to leak the box.
A more extreme option is to get rid of pthread mutexes entirely. (So, implement our mutex/rwlock/condvars directly using atomics and platform-specific syscalls, like we already do for on most platforms.) That's still somewhere on my to do list. ^^'
You mean like (not sure if there's an issue for that)?
Yeah, either parking_lot, or something other thread parker based, or maybe something not thread parker based but directly using a futex (or whatever the platform supports) might be a better fit for std. It's a bit tricky to make those trade offs for all std users at once, but not having to deal with all this pthread stuff (and not having to Box them, etc.) would be very nice.
Lowering priority to P-medium because of the previous discussion in .
I honestly think this is the only reasonable solution. POSIX’s semantics are not a good fit for us.
We discussed this in a recent library team meeting, and we'll be going forward with this.
Yay!!! That gives Rust an opportunity to have better performance than pthreads, too. The one caveat is robust mutexes, but I don’t think Rust needs those.
That effort is now tracked here:
This has been fixed on Linux. We no longer use pthread locks on Linux. :)

Related discussion:
text: /// /// It might be called more than once per LazyBox, as multiple threads /// might race to initialize it concurrently, each constructing and initializing <del> /// their own box. (All but one of them will be destroyed right after.) </del> <ins> /// their own box. All but one of them will be passed to `cancel_init` right after. </ins> fn init() -> Box<Self>; <ins> /// Any surplus boxes from `init()` that lost the initialization race /// are passed to this function for disposal. /// /// The default implementation calls destroy(). fn cancel_init(x: Box<Self>) { Self::destroy(x); } /// This is called to destroy a used box. /// /// The default implementation just drops it. fn destroy(_: Box<Self>) {} </ins> } impl<T: LazyInit> LazyBox<T> {