]> git.lizzy.rs Git - rust.git/commitdiff
std: Fix Windows XP compatibility
authorAlex Crichton <alex@alexcrichton.com>
Fri, 26 Jun 2015 16:30:35 +0000 (09:30 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Sun, 28 Jun 2015 02:45:24 +0000 (19:45 -0700)
This commit enables executables linked against the standard library to run on
Windows XP. There are two main components of this commit:

* APIs not available on XP are shimmed to have a fallback implementation and use
  runtime detection to determine if they are available.
* Mutexes on Windows were reimplemented to use critical sections on XP where
  rwlocks are not available.

The APIs which are not available on XP are:

* SetFileInformationByHandle - this is just used by `File::truncate` and that
  function just returns an error now.
* SetThreadStackGuarantee - this is used by the stack overflow support on
  windows, but if this isn't available then it's just ignored (it seems
  non-critical).
* All condition variable APIs are missing - the shims added for these apis
  simply always panic for now. We may eventually provide a fallback
  implementation, but for now the standard library does not rely on condition
  variables for normal use.
* RWLocks, like condition variables, are missing entirely. The same story for
  condition variables is taken here. These APIs are all now panicking stubs as
  the standard library doesn't rely on RWLocks for normal use.

Currently, as an optimization, we use SRWLOCKs for the standard `sync::Mutex`
implementation on Windows, which is indeed required for normal operation of the
standard library. To allow the standard library to run on XP, this commit
reimplements mutexes on Windows to use SRWLOCK instances *if available* and
otherwise a CriticalSection is used (with some checking for recursive
locking).

With all these changes put together, a 32-bit MSVC-built executable can run on
Windows XP and print "hello world"

Closes #12842
Closes #19992
Closes #24776

src/libstd/dynamic_lib.rs
src/libstd/sys/windows/c.rs
src/libstd/sys/windows/compat.rs [new file with mode: 0644]
src/libstd/sys/windows/condvar.rs
src/libstd/sys/windows/fs.rs
src/libstd/sys/windows/mod.rs
src/libstd/sys/windows/mutex.rs
src/libstd/sys/windows/rwlock.rs
src/libstd/sys/windows/stack_overflow.rs
src/libstd/sys/windows/sync.rs [deleted file]

index ddafe416305e11efc7fa44527c25cc7dcabb5961..a17d121e60a2a834c4928480d6d2d9191ec6d962 100644 (file)
@@ -263,7 +263,7 @@ mod dl {
     use sys::os;
     use os::windows::prelude::*;
     use ptr;
-    use sys::c::compat::kernel32::SetThreadErrorMode;
+    use sys::c::SetThreadErrorMode;
 
     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
         // disable "dll load failed" error dialog.
index 3c9b2ef1b986ecbef40a4201ed11c51771b410aa..7f89ea979391e251d3517090dd73ab8ff4e7c2f8 100644 (file)
@@ -13,6 +13,9 @@
 #![allow(bad_style, dead_code, overflowing_literals)]
 
 use libc;
+use libc::{c_uint, c_ulong};
+use libc::{DWORD, BOOL, BOOLEAN, ERROR_CALL_NOT_IMPLEMENTED, LPVOID, HANDLE};
+use libc::{LPCWSTR, LONG};
 
 pub use self::GET_FILEEX_INFO_LEVELS::*;
 pub use self::FILE_INFO_BY_HANDLE_CLASS::*;
@@ -240,7 +243,32 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
     pub PathBuffer: libc::WCHAR,
 }
 
+pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
+pub type PSRWLOCK = *mut SRWLOCK;
+pub type ULONG = c_ulong;
+pub type ULONG_PTR = c_ulong;
+
+#[repr(C)]
+pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
+#[repr(C)]
+pub struct SRWLOCK { pub ptr: LPVOID }
+#[repr(C)]
+pub struct CRITICAL_SECTION {
+    CriticalSectionDebug: LPVOID,
+    LockCount: LONG,
+    RecursionCount: LONG,
+    OwningThread: HANDLE,
+    LockSemaphore: HANDLE,
+    SpinCount: ULONG_PTR
+}
+
+pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE {
+    ptr: 0 as *mut _,
+};
+pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
+
 #[link(name = "ws2_32")]
+#[link(name = "userenv")]
 extern "system" {
     pub fn WSAStartup(wVersionRequested: libc::WORD,
                       lpWSAData: LPWSADATA) -> libc::c_int;
@@ -295,115 +323,13 @@ pub fn WaitForMultipleObjects(nCount: libc::DWORD,
     pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL;
     pub fn CancelIoEx(hFile: libc::HANDLE,
                       lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
-}
-
-pub mod compat {
-    use prelude::v1::*;
 
-    use ffi::CString;
-    use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
-    use sync::atomic::{AtomicUsize, Ordering};
+    pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
+    pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
+    pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
+    pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
+    pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
 
-    extern "system" {
-        fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
-        fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
-    }
-
-    fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
-                  fallback: usize) -> usize {
-        let mut module: Vec<u16> = module.utf16_units().collect();
-        module.push(0);
-        let symbol = CString::new(symbol).unwrap();
-        let func = unsafe {
-            let handle = GetModuleHandleW(module.as_ptr());
-            GetProcAddress(handle, symbol.as_ptr()) as usize
-        };
-        let value = if func == 0 {fallback} else {func};
-        ptr.store(value, Ordering::SeqCst);
-        value
-    }
-
-    /// Macro for creating a compatibility fallback for a Windows function
-    ///
-    /// # Examples
-    /// ```
-    /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
-    ///     // Fallback implementation
-    /// })
-    /// ```
-    ///
-    /// Note that arguments unused by the fallback implementation should not be
-    /// called `_` as they are used to be passed to the real function if
-    /// available.
-    macro_rules! compat_fn {
-        ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
-                                      -> $rettype:ty { $fallback:expr }) => (
-            #[inline(always)]
-            pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
-                use sync::atomic::{AtomicUsize, Ordering};
-                use mem;
-
-                static PTR: AtomicUsize = AtomicUsize::new(0);
-
-                fn load() -> usize {
-                    ::sys::c::compat::store_func(&PTR,
-                                                 stringify!($module),
-                                                 stringify!($symbol),
-                                                 fallback as usize)
-                }
-
-                extern "system" fn fallback($($argname: $argtype),*)
-                                            -> $rettype { $fallback }
-
-                let addr = match PTR.load(Ordering::SeqCst) {
-                    0 => load(),
-                    n => n,
-                };
-                let f: extern "system" fn($($argtype),*) -> $rettype =
-                    mem::transmute(addr);
-                f($($argname),*)
-            }
-        )
-    }
-
-    /// Compatibility layer for functions in `kernel32.dll`
-    ///
-    /// Latest versions of Windows this is needed for:
-    ///
-    /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
-    /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
-    pub mod kernel32 {
-        use libc::c_uint;
-        use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
-        use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
-        use sys::c::SetLastError;
-
-        compat_fn! {
-            kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
-                                          _lpTargetFileName: LPCWSTR,
-                                          _dwFlags: DWORD) -> BOOLEAN {
-                unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
-            }
-        }
-
-        compat_fn! {
-            kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
-                                                _lpszFilePath: LPCWSTR,
-                                                _cchFilePath: DWORD,
-                                                _dwFlags: DWORD) -> DWORD {
-                unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
-            }
-        }
-
-        compat_fn! {
-            kernel32::SetThreadErrorMode(_dwNewMode: DWORD, _lpOldMode: *mut DWORD) -> c_uint {
-                unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
-            }
-        }
-    }
-}
-
-extern "system" {
     // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL
     pub fn ReadConsoleW(hConsoleInput: libc::HANDLE,
                         lpBuffer: libc::LPVOID,
@@ -447,10 +373,6 @@ pub fn SetFileTime(hFile: libc::HANDLE,
                        lpCreationTime: *const libc::FILETIME,
                        lpLastAccessTime: *const libc::FILETIME,
                        lpLastWriteTime: *const libc::FILETIME) -> libc::BOOL;
-    pub fn SetFileInformationByHandle(hFile: libc::HANDLE,
-                    FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
-                    lpFileInformation: libc::LPVOID,
-                    dwBufferSize: libc::DWORD) -> libc::BOOL;
     pub fn GetTempPathW(nBufferLength: libc::DWORD,
                         lpBuffer: libc::LPCWSTR) -> libc::DWORD;
     pub fn OpenProcessToken(ProcessHandle: libc::HANDLE,
@@ -483,11 +405,70 @@ pub fn WaitForSingleObject(hHandle: libc::HANDLE,
     pub fn SwitchToThread() -> libc::BOOL;
     pub fn Sleep(dwMilliseconds: libc::DWORD);
     pub fn GetProcessId(handle: libc::HANDLE) -> libc::DWORD;
-}
-
-#[link(name = "userenv")]
-extern "system" {
     pub fn GetUserProfileDirectoryW(hToken: libc::HANDLE,
                                     lpProfileDir: libc::LPCWSTR,
                                     lpcchSize: *mut libc::DWORD) -> libc::BOOL;
 }
+
+// Functions that aren't available on Windows XP, but we still use them and just
+// provide some form of a fallback implementation.
+compat_fn! {
+    kernel32:
+
+    pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
+                               _lpTargetFileName: LPCWSTR,
+                               _dwFlags: DWORD) -> BOOLEAN {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
+    pub fn GetFinalPathNameByHandleW(_hFile: HANDLE,
+                                     _lpszFilePath: LPCWSTR,
+                                     _cchFilePath: DWORD,
+                                     _dwFlags: DWORD) -> DWORD {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
+    pub fn SetThreadErrorMode(_dwNewMode: DWORD,
+                              _lpOldMode: *mut DWORD) -> c_uint {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
+    pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
+    pub fn SetFileInformationByHandle(_hFile: HANDLE,
+                    _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
+                    _lpFileInformation: LPVOID,
+                    _dwBufferSize: DWORD) -> BOOL {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
+    }
+    pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE,
+                                     SRWLock: PSRWLOCK,
+                                     dwMilliseconds: DWORD,
+                                     Flags: ULONG) -> BOOL {
+        panic!("condition variables not available")
+    }
+    pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE)
+                                 -> () {
+        panic!("condition variables not available")
+    }
+    pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE)
+                                    -> () {
+        panic!("condition variables not available")
+    }
+    pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> () {
+        panic!("rwlocks not available")
+    }
+    pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK) -> () {
+        panic!("rwlocks not available")
+    }
+    pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK) -> () {
+        panic!("rwlocks not available")
+    }
+    pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK) -> () {
+        panic!("rwlocks not available")
+    }
+    pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN {
+        panic!("rwlocks not available")
+    }
+    pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN {
+        panic!("rwlocks not available")
+    }
+}
diff --git a/src/libstd/sys/windows/compat.rs b/src/libstd/sys/windows/compat.rs
new file mode 100644 (file)
index 0000000..3a03b91
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A "compatibility layer" for spanning XP and Windows 7
+//!
+//! The standard library currently binds many functions that are not available
+//! on Windows XP, but we would also like to support building executables that
+//! run on XP. To do this we specify all non-XP APIs as having a fallback
+//! implementation to do something reasonable.
+//!
+//! This dynamic runtime detection of whether a function is available is
+//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a
+//! static-per-function which caches the result of the first check. In this
+//! manner we pay a semi-large one-time cost up front for detecting whether a
+//! function is available but afterwards it's just a load and a jump.
+
+use prelude::v1::*;
+
+use ffi::CString;
+use libc::{LPVOID, LPCWSTR, HMODULE, LPCSTR};
+use sync::atomic::{AtomicUsize, Ordering};
+
+extern "system" {
+    fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
+    fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
+}
+
+pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
+    let mut module: Vec<u16> = module.utf16_units().collect();
+    module.push(0);
+    let symbol = CString::new(symbol).unwrap();
+    unsafe {
+        let handle = GetModuleHandleW(module.as_ptr());
+        match GetProcAddress(handle, symbol.as_ptr()) as usize {
+            0 => None,
+            n => Some(n),
+        }
+    }
+}
+
+pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
+                  fallback: usize) -> usize {
+    let value = lookup(module, symbol).unwrap_or(fallback);
+    ptr.store(value, Ordering::SeqCst);
+    value
+}
+
+macro_rules! compat_fn {
+    ($module:ident: $(
+        pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
+                                  -> $rettype:ty {
+            $($body:expr);*
+        }
+    )*) => ($(
+        #[allow(unused_variables)]
+        pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
+            use sync::atomic::{AtomicUsize, Ordering};
+            use mem;
+            type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
+
+            static PTR: AtomicUsize = AtomicUsize::new(0);
+
+            fn load() -> usize {
+                ::sys::compat::store_func(&PTR,
+                                          stringify!($module),
+                                          stringify!($symbol),
+                                          fallback as usize)
+            }
+            unsafe extern "system" fn fallback($($argname: $argtype),*)
+                                               -> $rettype {
+                $($body);*
+            }
+
+            let addr = match PTR.load(Ordering::SeqCst) {
+                0 => load(),
+                n => n,
+            };
+            mem::transmute::<usize, F>(addr)($($argname),*)
+        }
+    )*)
+}
index baa7d1ceea3316d41923f13ec0fafee8a5dbd6cc..04d62200e9bcc553777977cbe47171609972da6a 100644 (file)
 
 use cell::UnsafeCell;
 use libc::{self, DWORD};
-use sys::os;
+use sys::c;
 use sys::mutex::{self, Mutex};
-use sys::sync as ffi;
+use sys::os;
 use time::Duration;
 
-pub struct Condvar { inner: UnsafeCell<ffi::CONDITION_VARIABLE> }
+pub struct Condvar { inner: UnsafeCell<c::CONDITION_VARIABLE> }
 
 unsafe impl Send for Condvar {}
 unsafe impl Sync for Condvar {}
 
 impl Condvar {
     pub const fn new() -> Condvar {
-        Condvar { inner: UnsafeCell::new(ffi::CONDITION_VARIABLE_INIT) }
+        Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) }
     }
 
     #[inline]
     pub unsafe fn wait(&self, mutex: &Mutex) {
-        let r = ffi::SleepConditionVariableSRW(self.inner.get(),
-                                               mutex::raw(mutex),
-                                               libc::INFINITE,
-                                               0);
+        let r = c::SleepConditionVariableSRW(self.inner.get(),
+                                             mutex::raw(mutex),
+                                             libc::INFINITE,
+                                             0);
         debug_assert!(r != 0);
     }
 
     pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        let r = ffi::SleepConditionVariableSRW(self.inner.get(),
-                                               mutex::raw(mutex),
-                                               super::dur2timeout(dur),
-                                               0);
+        let r = c::SleepConditionVariableSRW(self.inner.get(),
+                                             mutex::raw(mutex),
+                                             super::dur2timeout(dur),
+                                             0);
         if r == 0 {
             const ERROR_TIMEOUT: DWORD = 0x5B4;
             debug_assert_eq!(os::errno() as usize, ERROR_TIMEOUT as usize);
@@ -52,12 +52,12 @@ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
 
     #[inline]
     pub unsafe fn notify_one(&self) {
-        ffi::WakeConditionVariable(self.inner.get())
+        c::WakeConditionVariable(self.inner.get())
     }
 
     #[inline]
     pub unsafe fn notify_all(&self) {
-        ffi::WakeAllConditionVariable(self.inner.get())
+        c::WakeAllConditionVariable(self.inner.get())
     }
 
     pub unsafe fn destroy(&self) {
index 437b2cc6491179e2020bdeeeb3244f880d994c3d..36fabe72aa0c1f70fb5665bf653aabee9780f246 100644 (file)
@@ -497,12 +497,11 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
 }
 
 pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
-    use sys::c::compat::kernel32::CreateSymbolicLinkW;
     let src = to_utf16(src);
     let dst = to_utf16(dst);
     let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
     try!(cvt(unsafe {
-        CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
+        c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
     }));
     Ok(())
 }
@@ -565,14 +564,13 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
 }
 
 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
-    use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
 
     let mut opts = OpenOptions::new();
     opts.read(true);
     let f = try!(File::open(p, &opts));
     super::fill_utf16_buf(|buf, sz| unsafe {
-        GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
-                                  libc::VOLUME_NAME_DOS)
+        c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
+                                     libc::VOLUME_NAME_DOS)
     }, |buf| {
         PathBuf::from(OsString::from_wide(buf))
     })
index 18c8add17a6d6d307103d93e6e00ac4c3805008d..b6d080109df053d4290f0c5f19a1fac3678fe625 100644 (file)
@@ -22,6 +22,8 @@
 use path::PathBuf;
 use time::Duration;
 
+#[macro_use] pub mod compat;
+
 pub mod backtrace;
 pub mod c;
 pub mod condvar;
@@ -36,7 +38,6 @@
 pub mod process;
 pub mod rwlock;
 pub mod stack_overflow;
-pub mod sync;
 pub mod thread;
 pub mod thread_local;
 pub mod time;
index 29e370698ad744b727f4c202c5d333a9ee6ea193..277c3d14c0ec5515e4a2396986ede72ac9cdae93 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! System Mutexes
+//!
+//! The Windows implementation of mutexes is a little odd and it may not be
+//! immediately obvious what's going on. The primary oddness is that SRWLock is
+//! used instead of CriticalSection, and this is done because:
+//!
+//! 1. SRWLock is several times faster than CriticalSection according to
+//!    benchmarks performed on both Windows 8 and Windows 7.
+//!
+//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The
+//!    Unix implementation deadlocks so consistency is preferred. See #19962 for
+//!    more details.
+//!
+//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
+//!    is there there are no guarantees of fairness.
+//!
+//! The downside of this approach, however, is that SRWLock is not available on
+//! Windows XP, so we continue to have a fallback implementation where
+//! CriticalSection is used and we keep track of who's holding the mutex to
+//! detect recursive locks.
+
 use prelude::v1::*;
 
 use cell::UnsafeCell;
-use sys::sync as ffi;
 use mem;
+use sync::atomic::{AtomicUsize, Ordering};
+use sys::c;
+use sys::compat;
 
-pub struct Mutex { inner: UnsafeCell<ffi::SRWLOCK> }
+pub struct Mutex {
+    lock: AtomicUsize,
+    held: UnsafeCell<bool>,
+}
 
 unsafe impl Send for Mutex {}
 unsafe impl Sync for Mutex {}
 
-#[inline]
-pub unsafe fn raw(m: &Mutex) -> ffi::PSRWLOCK {
-    m.inner.get()
+#[derive(Clone, Copy)]
+enum Kind {
+    SRWLock = 1,
+    CriticalSection = 2,
 }
 
-// So you might be asking why we're using SRWLock instead of CriticalSection?
-//
-// 1. SRWLock is several times faster than CriticalSection according to
-//    benchmarks performed on both Windows 8 and Windows 7.
-//
-// 2. CriticalSection allows recursive locking while SRWLock deadlocks. The Unix
-//    implementation deadlocks so consistency is preferred. See #19962 for more
-//    details.
-//
-// 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
-//    is there there are no guarantees of fairness.
+#[inline]
+pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK {
+    debug_assert!(mem::size_of::<c::SRWLOCK>() <= mem::size_of_val(&m.lock));
+    &m.lock as *const _ as *mut _
+}
 
 impl Mutex {
     pub const fn new() -> Mutex {
-        Mutex { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) }
+        Mutex {
+            lock: AtomicUsize::new(0),
+            held: UnsafeCell::new(false),
+        }
     }
-    #[inline]
     pub unsafe fn lock(&self) {
-        ffi::AcquireSRWLockExclusive(self.inner.get())
+        match kind() {
+            Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
+            Kind::CriticalSection => {
+                let re = self.remutex();
+                (*re).lock();
+                if !self.flag_locked() {
+                    (*re).unlock();
+                    panic!("cannot recursively lock a mutex");
+                }
+            }
+        }
     }
-    #[inline]
     pub unsafe fn try_lock(&self) -> bool {
-        ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0
+        match kind() {
+            Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0,
+            Kind::CriticalSection => {
+                let re = self.remutex();
+                if !(*re).try_lock() {
+                    false
+                } else if self.flag_locked() {
+                    true
+                } else {
+                    (*re).unlock();
+                    false
+                }
+            }
+        }
     }
-    #[inline]
     pub unsafe fn unlock(&self) {
-        ffi::ReleaseSRWLockExclusive(self.inner.get())
+        *self.held.get() = false;
+        match kind() {
+            Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)),
+            Kind::CriticalSection => (*self.remutex()).unlock(),
+        }
     }
-    #[inline]
     pub unsafe fn destroy(&self) {
-        // ...
+        match kind() {
+            Kind::SRWLock => {}
+            Kind::CriticalSection => {
+                match self.lock.load(Ordering::SeqCst) {
+                    0 => {}
+                    n => { Box::from_raw(n as *mut ReentrantMutex).destroy(); }
+                }
+            }
+        }
+    }
+
+    unsafe fn remutex(&self) -> *mut ReentrantMutex {
+        match self.lock.load(Ordering::SeqCst) {
+            0 => {}
+            n => return n as *mut _,
+        }
+        let mut re = Box::new(ReentrantMutex::uninitialized());
+        re.init();
+        let re = Box::into_raw(re);
+        match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) {
+            0 => re,
+            n => { Box::from_raw(re).destroy(); n as *mut _ }
+        }
+    }
+
+    unsafe fn flag_locked(&self) -> bool {
+        if *self.held.get() {
+            false
+        } else {
+            *self.held.get() = true;
+            true
+        }
+
     }
 }
 
-pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }
+fn kind() -> Kind {
+    static KIND: AtomicUsize = AtomicUsize::new(0);
+
+    let val = KIND.load(Ordering::SeqCst);
+    if val == Kind::SRWLock as usize {
+        return Kind::SRWLock
+    } else if val == Kind::CriticalSection as usize {
+        return Kind::CriticalSection
+    }
+
+    let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
+        None => Kind::CriticalSection,
+        Some(..) => Kind::SRWLock,
+    };
+    KIND.store(ret as usize, Ordering::SeqCst);
+    return ret;
+}
+
+pub struct ReentrantMutex { inner: UnsafeCell<c::CRITICAL_SECTION> }
 
 unsafe impl Send for ReentrantMutex {}
 unsafe impl Sync for ReentrantMutex {}
@@ -69,23 +166,23 @@ pub unsafe fn uninitialized() -> ReentrantMutex {
     }
 
     pub unsafe fn init(&mut self) {
-        ffi::InitializeCriticalSection(self.inner.get());
+        c::InitializeCriticalSection(self.inner.get());
     }
 
     pub unsafe fn lock(&self) {
-        ffi::EnterCriticalSection(self.inner.get());
+        c::EnterCriticalSection(self.inner.get());
     }
 
     #[inline]
     pub unsafe fn try_lock(&self) -> bool {
-        ffi::TryEnterCriticalSection(self.inner.get()) != 0
+        c::TryEnterCriticalSection(self.inner.get()) != 0
     }
 
     pub unsafe fn unlock(&self) {
-        ffi::LeaveCriticalSection(self.inner.get());
+        c::LeaveCriticalSection(self.inner.get());
     }
 
     pub unsafe fn destroy(&self) {
-        ffi::DeleteCriticalSection(self.inner.get());
+        c::DeleteCriticalSection(self.inner.get());
     }
 }
index e727638e3e9b5cddf67a2dfd14792649627a1080..25865286db051aba32e4f60c33b191efc35cb00b 100644 (file)
 use prelude::v1::*;
 
 use cell::UnsafeCell;
-use sys::sync as ffi;
+use sys::c;
 
-pub struct RWLock { inner: UnsafeCell<ffi::SRWLOCK> }
+pub struct RWLock { inner: UnsafeCell<c::SRWLOCK> }
 
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {}
 
 impl RWLock {
     pub const fn new() -> RWLock {
-        RWLock { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) }
+        RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) }
     }
     #[inline]
     pub unsafe fn read(&self) {
-        ffi::AcquireSRWLockShared(self.inner.get())
+        c::AcquireSRWLockShared(self.inner.get())
     }
     #[inline]
     pub unsafe fn try_read(&self) -> bool {
-        ffi::TryAcquireSRWLockShared(self.inner.get()) != 0
+        c::TryAcquireSRWLockShared(self.inner.get()) != 0
     }
     #[inline]
     pub unsafe fn write(&self) {
-        ffi::AcquireSRWLockExclusive(self.inner.get())
+        c::AcquireSRWLockExclusive(self.inner.get())
     }
     #[inline]
     pub unsafe fn try_write(&self) -> bool {
-        ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0
+        c::TryAcquireSRWLockExclusive(self.inner.get()) != 0
     }
     #[inline]
     pub unsafe fn read_unlock(&self) {
-        ffi::ReleaseSRWLockShared(self.inner.get())
+        c::ReleaseSRWLockShared(self.inner.get())
     }
     #[inline]
     pub unsafe fn write_unlock(&self) {
-        ffi::ReleaseSRWLockExclusive(self.inner.get())
+        c::ReleaseSRWLockExclusive(self.inner.get())
     }
 
     #[inline]
index 79b7de4f341ac07280d7d69a5c28faa44266f75d..cf827848db5c99f0f70de6295220a007ecb4fb76 100644 (file)
@@ -8,12 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rt::util::report_overflow;
 use core::prelude::*;
-use ptr;
-use mem;
+
+use libc::types::os::arch::extra::{LPVOID, DWORD, LONG};
 use libc;
-use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
+use mem;
+use ptr;
+use rt::util::report_overflow;
+use sys::c;
 use sys_common::stack;
 
 pub struct Handler {
@@ -69,8 +71,12 @@ pub unsafe fn cleanup() {
 }
 
 pub unsafe fn make_handler() -> Handler {
-    if SetThreadStackGuarantee(&mut 0x5000) == 0 {
-        panic!("failed to reserve stack space for exception handling");
+    // This API isn't available on XP, so don't panic in that case and just pray
+    // it works out ok.
+    if c::SetThreadStackGuarantee(&mut 0x5000) == 0 {
+        if libc::GetLastError() as u32 != libc::ERROR_CALL_NOT_IMPLEMENTED as u32 {
+            panic!("failed to reserve stack space for exception handling");
+        }
     }
 
     Handler { _data: 0 as *mut libc::c_void }
@@ -103,5 +109,4 @@ pub struct EXCEPTION_POINTERS {
     fn AddVectoredExceptionHandler(FirstHandler: ULONG,
                                    VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
                                   -> LPVOID;
-    fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL;
 }
diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs
deleted file mode 100644 (file)
index 5410259..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use libc::{BOOL, DWORD, LPVOID, LONG, HANDLE, c_ulong};
-use libc::types::os::arch::extra::BOOLEAN;
-
-pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
-pub type PSRWLOCK = *mut SRWLOCK;
-pub type ULONG = c_ulong;
-pub type ULONG_PTR = c_ulong;
-
-#[repr(C)]
-pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
-#[repr(C)]
-pub struct SRWLOCK { pub ptr: LPVOID }
-#[repr(C)]
-pub struct CRITICAL_SECTION {
-    CriticalSectionDebug: LPVOID,
-    LockCount: LONG,
-    RecursionCount: LONG,
-    OwningThread: HANDLE,
-    LockSemaphore: HANDLE,
-    SpinCount: ULONG_PTR
-}
-
-pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE {
-    ptr: 0 as *mut _,
-};
-pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
-
-extern "system" {
-    // condition variables
-    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);
-
-    // slim rwlocks
-    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;
-
-    pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
-    pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
-    pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
-    pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
-    pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
-}