Change:
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; extern "system" { <del> // critical sections pub fn InitializeCriticalSectionAndSpinCount( lpCriticalSection: LPCRITICAL_SECTION, dwSpinCount: DWORD) -> BOOL; pub fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); pub fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); pub fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); pub fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; </del> // condition variables <del> pub fn SleepConditionVariableCS(ConditionVariable: LPCONDITION_VARIABLE, CriticalSection: LPCRITICAL_SECTION, dwMilliseconds: DWORD) -> BOOL; pub fn WakeConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); pub fn WakeAllConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); </del> <ins> pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, SRWLock: PSRWLOCK, dwMilliseconds: DWORD, Flags: ULONG) -> BOOL; pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE); pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE); </ins> // slim rwlocks <del> pub fn AcquireSRWLockExclusive(SRWLock: LPSRWLOCK); pub fn AcquireSRWLockShared(SRWLock: LPSRWLOCK); pub fn ReleaseSRWLockExclusive(SRWLock: LPSRWLOCK); pub fn ReleaseSRWLockShared(SRWLock: LPSRWLOCK); pub fn TryAcquireSRWLockExclusive(SRWLock: LPSRWLOCK) -> BOOLEAN; pub fn TryAcquireSRWLockShared(SRWLock: LPSRWLOCK) -> BOOLEAN; </del> <ins> pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK); pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK); pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK); pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK); pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN; pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN; </ins> }

Discussion:
This behavioral difference should factor into the stabilization of the methods on and how we'd like to word the documentation. For now though, this is a tracking issue as this difference in behavior was not originally intended. cc
How can this make the semantics of the API differ substantially? You have a borrow of the value protected by the Mutex, so even if the is reentrant I don't see how you could expose that through the API. Or I guess the issue is that you could get aliased s by ing a on Windows and unlocking both in the same thread?
Unfortunately the borrow is earned from the structure, not the , which allows you lock it twice. For example, if this code completed, it could exhibit memory unsafety (two references): On unix that code deadlocks, but on windows it executes just fine.
Wait, doesn't take a mutable reference to the ? Weird, isn't that guaranteed to deadlock on Unix? Anyway, since you could have aliased s in the same thread even if that were changed, I think Windows mutexes need an additional check for safety (Windows fast mutexes look like they have too many limitations to be a drop in replacement).
can't take . That'd be a guarantee that the caller has exclusive access to the mutex, which defeats the entire point.
Oh right, it has to be shared in the first place. Heh.
What are the limitations of fast mutexes that prevent us from using them?
and I discussed this and he recommends just adding a flag and checking it manually on Windows.
I don't think there are any limitations (we're currently using them), this is just a surprising consequence of using them. yeah I suspect that will have specific code on windows for a separate which is inspected once the lock is acquired (using ) and if the same thread acquires the mutex twice it just panics.
According to the current code we use Critical Sections for Mutexes on Windows. Fast/Guarded mutexes are a separate thing entirely and will deadlock if you attempt to lock them twice on the same thread. The MSVC C++ implementation for checks if the locked thread is the current thread before even trying to lock it and throws an exception if it is.
Whoa! I had no idea that "fast mutexes" was a thing. I thought there were only and .

It looks like the "fast mutexes" or "guarded mutexes" are only for drivers, and maybe not for userland code? Or at least I couldn't figure out how to use them. Do you have pointers to the source for MSVC's implementation, or how to use them?
Yeah, the bits that made it seem like they were only for drivers was why I said we probably couldn't use them. Apparently critical sections are the fastest userland ones you should use. On a related note, it might be worth clarifying in the documentation that they aren't intended to be used between processes (maybe it's already there, but I don't recall seeing it).
According to some debugging, on Windows 8.1 x64, MSVC uses for instead of . I'm going to do some benchmarking to determine their performance difference. As an bonus, you can sleep a on an in Windows which Rust could provide as a platform specific extension trait.
And this is pretty conclusive. I did a test with two threads constantly locking and unlocking the same mutex. When I used it took about 260ns per lock+unlock. When I used it took about 60ns per lock+unlock.
On my beefy Win7 x64 gaming rig, I get a rock-solid 26ns for RW, and a fluctuating 70-80ns for Critical. Faster and more reliable.
Oh wow, and they can't be acquired recursively either, so we wouldn't even need to do anything different from the other platforms. Good find!
- Yep just tried acquiring an twice on the same thread and it has deadlocked.
Hm, they look like they have other problems though: Defaulting to an unfair lock doesn't seem like a good idea (unless your only goal is to win benchmarks). But if MSVC++ is doing it...
We already use for in Rust anyway.
what's the simple solution you have in mind? (Storing an extra flag internally?)
Some quick tests show that is indeed fairer than . CriticalSection: SRWLock: Each row is 100ms apart. CPU has 4 cores/threads.
If you write a semaphore on top of it and hard-wire the count at 1, it will deadlock. There is probably a better way to do it.

I think the older mutex type is already non-recursive but I think it's missing the syscall free fast path.
As of Windows Server 2003 SP1, doesn't use FIFO, which means it doesn't actually guarantee fairness. If we're going to provide fair mutexes then we need to provide a solid fairness guarantee otherwise we might as well just use whatever is fastest.
After some clarification on IRC I have realized that fairness means low priority threads won't steal mutex lock time from higher priority threads.
Apparently [] is non-recursive on Linux too, and easy to deadlock: