Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 10b103a

Browse files
committedJun 28, 2015
std: Fix Windows XP compatibility
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
1 parent 8790958 commit 10b103a

File tree

10 files changed

+353
-243
lines changed

10 files changed

+353
-243
lines changed
 

‎src/libstd/dynamic_lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ mod dl {
263263
use sys::os;
264264
use os::windows::prelude::*;
265265
use ptr;
266-
use sys::c::compat::kernel32::SetThreadErrorMode;
266+
use sys::c::SetThreadErrorMode;
267267

268268
pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
269269
// disable "dll load failed" error dialog.

‎src/libstd/sys/windows/c.rs

Lines changed: 96 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#![allow(bad_style, dead_code, overflowing_literals)]
1414

1515
use libc;
16+
use libc::{c_uint, c_ulong};
17+
use libc::{DWORD, BOOL, BOOLEAN, ERROR_CALL_NOT_IMPLEMENTED, LPVOID, HANDLE};
18+
use libc::{LPCWSTR, LONG};
1619

1720
pub use self::GET_FILEEX_INFO_LEVELS::*;
1821
pub use self::FILE_INFO_BY_HANDLE_CLASS::*;
@@ -240,7 +243,32 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER {
240243
pub PathBuffer: libc::WCHAR,
241244
}
242245

246+
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
247+
pub type PSRWLOCK = *mut SRWLOCK;
248+
pub type ULONG = c_ulong;
249+
pub type ULONG_PTR = c_ulong;
250+
251+
#[repr(C)]
252+
pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
253+
#[repr(C)]
254+
pub struct SRWLOCK { pub ptr: LPVOID }
255+
#[repr(C)]
256+
pub struct CRITICAL_SECTION {
257+
CriticalSectionDebug: LPVOID,
258+
LockCount: LONG,
259+
RecursionCount: LONG,
260+
OwningThread: HANDLE,
261+
LockSemaphore: HANDLE,
262+
SpinCount: ULONG_PTR
263+
}
264+
265+
pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE {
266+
ptr: 0 as *mut _,
267+
};
268+
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
269+
243270
#[link(name = "ws2_32")]
271+
#[link(name = "userenv")]
244272
extern "system" {
245273
pub fn WSAStartup(wVersionRequested: libc::WORD,
246274
lpWSAData: LPWSADATA) -> libc::c_int;
@@ -295,115 +323,13 @@ extern "system" {
295323
pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL;
296324
pub fn CancelIoEx(hFile: libc::HANDLE,
297325
lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
298-
}
299-
300-
pub mod compat {
301-
use prelude::v1::*;
302326

303-
use ffi::CString;
304-
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
305-
use sync::atomic::{AtomicUsize, Ordering};
327+
pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
328+
pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
329+
pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
330+
pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
331+
pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
306332

307-
extern "system" {
308-
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
309-
fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
310-
}
311-
312-
fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
313-
fallback: usize) -> usize {
314-
let mut module: Vec<u16> = module.utf16_units().collect();
315-
module.push(0);
316-
let symbol = CString::new(symbol).unwrap();
317-
let func = unsafe {
318-
let handle = GetModuleHandleW(module.as_ptr());
319-
GetProcAddress(handle, symbol.as_ptr()) as usize
320-
};
321-
let value = if func == 0 {fallback} else {func};
322-
ptr.store(value, Ordering::SeqCst);
323-
value
324-
}
325-
326-
/// Macro for creating a compatibility fallback for a Windows function
327-
///
328-
/// # Examples
329-
/// ```
330-
/// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
331-
/// // Fallback implementation
332-
/// })
333-
/// ```
334-
///
335-
/// Note that arguments unused by the fallback implementation should not be
336-
/// called `_` as they are used to be passed to the real function if
337-
/// available.
338-
macro_rules! compat_fn {
339-
($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
340-
-> $rettype:ty { $fallback:expr }) => (
341-
#[inline(always)]
342-
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
343-
use sync::atomic::{AtomicUsize, Ordering};
344-
use mem;
345-
346-
static PTR: AtomicUsize = AtomicUsize::new(0);
347-
348-
fn load() -> usize {
349-
::sys::c::compat::store_func(&PTR,
350-
stringify!($module),
351-
stringify!($symbol),
352-
fallback as usize)
353-
}
354-
355-
extern "system" fn fallback($($argname: $argtype),*)
356-
-> $rettype { $fallback }
357-
358-
let addr = match PTR.load(Ordering::SeqCst) {
359-
0 => load(),
360-
n => n,
361-
};
362-
let f: extern "system" fn($($argtype),*) -> $rettype =
363-
mem::transmute(addr);
364-
f($($argname),*)
365-
}
366-
)
367-
}
368-
369-
/// Compatibility layer for functions in `kernel32.dll`
370-
///
371-
/// Latest versions of Windows this is needed for:
372-
///
373-
/// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
374-
/// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
375-
pub mod kernel32 {
376-
use libc::c_uint;
377-
use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
378-
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
379-
use sys::c::SetLastError;
380-
381-
compat_fn! {
382-
kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
383-
_lpTargetFileName: LPCWSTR,
384-
_dwFlags: DWORD) -> BOOLEAN {
385-
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
386-
}
387-
}
388-
389-
compat_fn! {
390-
kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
391-
_lpszFilePath: LPCWSTR,
392-
_cchFilePath: DWORD,
393-
_dwFlags: DWORD) -> DWORD {
394-
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
395-
}
396-
}
397-
398-
compat_fn! {
399-
kernel32::SetThreadErrorMode(_dwNewMode: DWORD, _lpOldMode: *mut DWORD) -> c_uint {
400-
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
401-
}
402-
}
403-
}
404-
}
405-
406-
extern "system" {
407333
// FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL
408334
pub fn ReadConsoleW(hConsoleInput: libc::HANDLE,
409335
lpBuffer: libc::LPVOID,
@@ -447,10 +373,6 @@ extern "system" {
447373
lpCreationTime: *const libc::FILETIME,
448374
lpLastAccessTime: *const libc::FILETIME,
449375
lpLastWriteTime: *const libc::FILETIME) -> libc::BOOL;
450-
pub fn SetFileInformationByHandle(hFile: libc::HANDLE,
451-
FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
452-
lpFileInformation: libc::LPVOID,
453-
dwBufferSize: libc::DWORD) -> libc::BOOL;
454376
pub fn GetTempPathW(nBufferLength: libc::DWORD,
455377
lpBuffer: libc::LPCWSTR) -> libc::DWORD;
456378
pub fn OpenProcessToken(ProcessHandle: libc::HANDLE,
@@ -483,11 +405,70 @@ extern "system" {
483405
pub fn SwitchToThread() -> libc::BOOL;
484406
pub fn Sleep(dwMilliseconds: libc::DWORD);
485407
pub fn GetProcessId(handle: libc::HANDLE) -> libc::DWORD;
486-
}
487-
488-
#[link(name = "userenv")]
489-
extern "system" {
490408
pub fn GetUserProfileDirectoryW(hToken: libc::HANDLE,
491409
lpProfileDir: libc::LPCWSTR,
492410
lpcchSize: *mut libc::DWORD) -> libc::BOOL;
493411
}
412+
413+
// Functions that aren't available on Windows XP, but we still use them and just
414+
// provide some form of a fallback implementation.
415+
compat_fn! {
416+
kernel32:
417+
418+
pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
419+
_lpTargetFileName: LPCWSTR,
420+
_dwFlags: DWORD) -> BOOLEAN {
421+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
422+
}
423+
pub fn GetFinalPathNameByHandleW(_hFile: HANDLE,
424+
_lpszFilePath: LPCWSTR,
425+
_cchFilePath: DWORD,
426+
_dwFlags: DWORD) -> DWORD {
427+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
428+
}
429+
pub fn SetThreadErrorMode(_dwNewMode: DWORD,
430+
_lpOldMode: *mut DWORD) -> c_uint {
431+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
432+
}
433+
pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL {
434+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
435+
}
436+
pub fn SetFileInformationByHandle(_hFile: HANDLE,
437+
_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
438+
_lpFileInformation: LPVOID,
439+
_dwBufferSize: DWORD) -> BOOL {
440+
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
441+
}
442+
pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE,
443+
SRWLock: PSRWLOCK,
444+
dwMilliseconds: DWORD,
445+
Flags: ULONG) -> BOOL {
446+
panic!("condition variables not available")
447+
}
448+
pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE)
449+
-> () {
450+
panic!("condition variables not available")
451+
}
452+
pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE)
453+
-> () {
454+
panic!("condition variables not available")
455+
}
456+
pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> () {
457+
panic!("rwlocks not available")
458+
}
459+
pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK) -> () {
460+
panic!("rwlocks not available")
461+
}
462+
pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK) -> () {
463+
panic!("rwlocks not available")
464+
}
465+
pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK) -> () {
466+
panic!("rwlocks not available")
467+
}
468+
pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN {
469+
panic!("rwlocks not available")
470+
}
471+
pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN {
472+
panic!("rwlocks not available")
473+
}
474+
}

‎src/libstd/sys/windows/compat.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! A "compatibility layer" for spanning XP and Windows 7
12+
//!
13+
//! The standard library currently binds many functions that are not available
14+
//! on Windows XP, but we would also like to support building executables that
15+
//! run on XP. To do this we specify all non-XP APIs as having a fallback
16+
//! implementation to do something reasonable.
17+
//!
18+
//! This dynamic runtime detection of whether a function is available is
19+
//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a
20+
//! static-per-function which caches the result of the first check. In this
21+
//! manner we pay a semi-large one-time cost up front for detecting whether a
22+
//! function is available but afterwards it's just a load and a jump.
23+
24+
use prelude::v1::*;
25+
26+
use ffi::CString;
27+
use libc::{LPVOID, LPCWSTR, HMODULE, LPCSTR};
28+
use sync::atomic::{AtomicUsize, Ordering};
29+
30+
extern "system" {
31+
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
32+
fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
33+
}
34+
35+
pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
36+
let mut module: Vec<u16> = module.utf16_units().collect();
37+
module.push(0);
38+
let symbol = CString::new(symbol).unwrap();
39+
unsafe {
40+
let handle = GetModuleHandleW(module.as_ptr());
41+
match GetProcAddress(handle, symbol.as_ptr()) as usize {
42+
0 => None,
43+
n => Some(n),
44+
}
45+
}
46+
}
47+
48+
pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
49+
fallback: usize) -> usize {
50+
let value = lookup(module, symbol).unwrap_or(fallback);
51+
ptr.store(value, Ordering::SeqCst);
52+
value
53+
}
54+
55+
macro_rules! compat_fn {
56+
($module:ident: $(
57+
pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
58+
-> $rettype:ty {
59+
$($body:expr);*
60+
}
61+
)*) => ($(
62+
#[allow(unused_variables)]
63+
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
64+
use sync::atomic::{AtomicUsize, Ordering};
65+
use mem;
66+
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
67+
68+
static PTR: AtomicUsize = AtomicUsize::new(0);
69+
70+
fn load() -> usize {
71+
::sys::compat::store_func(&PTR,
72+
stringify!($module),
73+
stringify!($symbol),
74+
fallback as usize)
75+
}
76+
unsafe extern "system" fn fallback($($argname: $argtype),*)
77+
-> $rettype {
78+
$($body);*
79+
}
80+
81+
let addr = match PTR.load(Ordering::SeqCst) {
82+
0 => load(),
83+
n => n,
84+
};
85+
mem::transmute::<usize, F>(addr)($($argname),*)
86+
}
87+
)*)
88+
}

‎src/libstd/sys/windows/condvar.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,35 @@ use prelude::v1::*;
1212

1313
use cell::UnsafeCell;
1414
use libc::{self, DWORD};
15-
use sys::os;
15+
use sys::c;
1616
use sys::mutex::{self, Mutex};
17-
use sys::sync as ffi;
17+
use sys::os;
1818
use time::Duration;
1919

20-
pub struct Condvar { inner: UnsafeCell<ffi::CONDITION_VARIABLE> }
20+
pub struct Condvar { inner: UnsafeCell<c::CONDITION_VARIABLE> }
2121

2222
unsafe impl Send for Condvar {}
2323
unsafe impl Sync for Condvar {}
2424

2525
impl Condvar {
2626
pub const fn new() -> Condvar {
27-
Condvar { inner: UnsafeCell::new(ffi::CONDITION_VARIABLE_INIT) }
27+
Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) }
2828
}
2929

3030
#[inline]
3131
pub unsafe fn wait(&self, mutex: &Mutex) {
32-
let r = ffi::SleepConditionVariableSRW(self.inner.get(),
33-
mutex::raw(mutex),
34-
libc::INFINITE,
35-
0);
32+
let r = c::SleepConditionVariableSRW(self.inner.get(),
33+
mutex::raw(mutex),
34+
libc::INFINITE,
35+
0);
3636
debug_assert!(r != 0);
3737
}
3838

3939
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
40-
let r = ffi::SleepConditionVariableSRW(self.inner.get(),
41-
mutex::raw(mutex),
42-
super::dur2timeout(dur),
43-
0);
40+
let r = c::SleepConditionVariableSRW(self.inner.get(),
41+
mutex::raw(mutex),
42+
super::dur2timeout(dur),
43+
0);
4444
if r == 0 {
4545
const ERROR_TIMEOUT: DWORD = 0x5B4;
4646
debug_assert_eq!(os::errno() as usize, ERROR_TIMEOUT as usize);
@@ -52,12 +52,12 @@ impl Condvar {
5252

5353
#[inline]
5454
pub unsafe fn notify_one(&self) {
55-
ffi::WakeConditionVariable(self.inner.get())
55+
c::WakeConditionVariable(self.inner.get())
5656
}
5757

5858
#[inline]
5959
pub unsafe fn notify_all(&self) {
60-
ffi::WakeAllConditionVariable(self.inner.get())
60+
c::WakeAllConditionVariable(self.inner.get())
6161
}
6262

6363
pub unsafe fn destroy(&self) {

‎src/libstd/sys/windows/fs.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,12 +497,11 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
497497
}
498498

499499
pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
500-
use sys::c::compat::kernel32::CreateSymbolicLinkW;
501500
let src = to_utf16(src);
502501
let dst = to_utf16(dst);
503502
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
504503
try!(cvt(unsafe {
505-
CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
504+
c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
506505
}));
507506
Ok(())
508507
}
@@ -565,14 +564,13 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
565564
}
566565

567566
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
568-
use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
569567

570568
let mut opts = OpenOptions::new();
571569
opts.read(true);
572570
let f = try!(File::open(p, &opts));
573571
super::fill_utf16_buf(|buf, sz| unsafe {
574-
GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
575-
libc::VOLUME_NAME_DOS)
572+
c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
573+
libc::VOLUME_NAME_DOS)
576574
}, |buf| {
577575
PathBuf::from(OsString::from_wide(buf))
578576
})

‎src/libstd/sys/windows/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use os::windows::ffi::{OsStrExt, OsStringExt};
2222
use path::PathBuf;
2323
use time::Duration;
2424

25+
#[macro_use] pub mod compat;
26+
2527
pub mod backtrace;
2628
pub mod c;
2729
pub mod condvar;
@@ -36,7 +38,6 @@ pub mod pipe;
3638
pub mod process;
3739
pub mod rwlock;
3840
pub mod stack_overflow;
39-
pub mod sync;
4041
pub mod thread;
4142
pub mod thread_local;
4243
pub mod time;

‎src/libstd/sys/windows/mutex.rs

Lines changed: 128 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,154 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
//! System Mutexes
12+
//!
13+
//! The Windows implementation of mutexes is a little odd and it may not be
14+
//! immediately obvious what's going on. The primary oddness is that SRWLock is
15+
//! used instead of CriticalSection, and this is done because:
16+
//!
17+
//! 1. SRWLock is several times faster than CriticalSection according to
18+
//! benchmarks performed on both Windows 8 and Windows 7.
19+
//!
20+
//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The
21+
//! Unix implementation deadlocks so consistency is preferred. See #19962 for
22+
//! more details.
23+
//!
24+
//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
25+
//! is there there are no guarantees of fairness.
26+
//!
27+
//! The downside of this approach, however, is that SRWLock is not available on
28+
//! Windows XP, so we continue to have a fallback implementation where
29+
//! CriticalSection is used and we keep track of who's holding the mutex to
30+
//! detect recursive locks.
31+
1132
use prelude::v1::*;
1233

1334
use cell::UnsafeCell;
14-
use sys::sync as ffi;
1535
use mem;
36+
use sync::atomic::{AtomicUsize, Ordering};
37+
use sys::c;
38+
use sys::compat;
1639

17-
pub struct Mutex { inner: UnsafeCell<ffi::SRWLOCK> }
40+
pub struct Mutex {
41+
lock: AtomicUsize,
42+
held: UnsafeCell<bool>,
43+
}
1844

1945
unsafe impl Send for Mutex {}
2046
unsafe impl Sync for Mutex {}
2147

22-
#[inline]
23-
pub unsafe fn raw(m: &Mutex) -> ffi::PSRWLOCK {
24-
m.inner.get()
48+
#[derive(Clone, Copy)]
49+
enum Kind {
50+
SRWLock = 1,
51+
CriticalSection = 2,
2552
}
2653

27-
// So you might be asking why we're using SRWLock instead of CriticalSection?
28-
//
29-
// 1. SRWLock is several times faster than CriticalSection according to
30-
// benchmarks performed on both Windows 8 and Windows 7.
31-
//
32-
// 2. CriticalSection allows recursive locking while SRWLock deadlocks. The Unix
33-
// implementation deadlocks so consistency is preferred. See #19962 for more
34-
// details.
35-
//
36-
// 3. While CriticalSection is fair and SRWLock is not, the current Rust policy
37-
// is there there are no guarantees of fairness.
54+
#[inline]
55+
pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK {
56+
debug_assert!(mem::size_of::<c::SRWLOCK>() <= mem::size_of_val(&m.lock));
57+
&m.lock as *const _ as *mut _
58+
}
3859

3960
impl Mutex {
4061
pub const fn new() -> Mutex {
41-
Mutex { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) }
62+
Mutex {
63+
lock: AtomicUsize::new(0),
64+
held: UnsafeCell::new(false),
65+
}
4266
}
43-
#[inline]
4467
pub unsafe fn lock(&self) {
45-
ffi::AcquireSRWLockExclusive(self.inner.get())
68+
match kind() {
69+
Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
70+
Kind::CriticalSection => {
71+
let re = self.remutex();
72+
(*re).lock();
73+
if !self.flag_locked() {
74+
(*re).unlock();
75+
panic!("cannot recursively lock a mutex");
76+
}
77+
}
78+
}
4679
}
47-
#[inline]
4880
pub unsafe fn try_lock(&self) -> bool {
49-
ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0
81+
match kind() {
82+
Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0,
83+
Kind::CriticalSection => {
84+
let re = self.remutex();
85+
if !(*re).try_lock() {
86+
false
87+
} else if self.flag_locked() {
88+
true
89+
} else {
90+
(*re).unlock();
91+
false
92+
}
93+
}
94+
}
5095
}
51-
#[inline]
5296
pub unsafe fn unlock(&self) {
53-
ffi::ReleaseSRWLockExclusive(self.inner.get())
97+
*self.held.get() = false;
98+
match kind() {
99+
Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)),
100+
Kind::CriticalSection => (*self.remutex()).unlock(),
101+
}
54102
}
55-
#[inline]
56103
pub unsafe fn destroy(&self) {
57-
// ...
104+
match kind() {
105+
Kind::SRWLock => {}
106+
Kind::CriticalSection => {
107+
match self.lock.load(Ordering::SeqCst) {
108+
0 => {}
109+
n => { Box::from_raw(n as *mut ReentrantMutex).destroy(); }
110+
}
111+
}
112+
}
113+
}
114+
115+
unsafe fn remutex(&self) -> *mut ReentrantMutex {
116+
match self.lock.load(Ordering::SeqCst) {
117+
0 => {}
118+
n => return n as *mut _,
119+
}
120+
let mut re = Box::new(ReentrantMutex::uninitialized());
121+
re.init();
122+
let re = Box::into_raw(re);
123+
match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) {
124+
0 => re,
125+
n => { Box::from_raw(re).destroy(); n as *mut _ }
126+
}
127+
}
128+
129+
unsafe fn flag_locked(&self) -> bool {
130+
if *self.held.get() {
131+
false
132+
} else {
133+
*self.held.get() = true;
134+
true
135+
}
136+
58137
}
59138
}
60139

61-
pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }
140+
fn kind() -> Kind {
141+
static KIND: AtomicUsize = AtomicUsize::new(0);
142+
143+
let val = KIND.load(Ordering::SeqCst);
144+
if val == Kind::SRWLock as usize {
145+
return Kind::SRWLock
146+
} else if val == Kind::CriticalSection as usize {
147+
return Kind::CriticalSection
148+
}
149+
150+
let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
151+
None => Kind::CriticalSection,
152+
Some(..) => Kind::SRWLock,
153+
};
154+
KIND.store(ret as usize, Ordering::SeqCst);
155+
return ret;
156+
}
157+
158+
pub struct ReentrantMutex { inner: UnsafeCell<c::CRITICAL_SECTION> }
62159

63160
unsafe impl Send for ReentrantMutex {}
64161
unsafe impl Sync for ReentrantMutex {}
@@ -69,23 +166,23 @@ impl ReentrantMutex {
69166
}
70167

71168
pub unsafe fn init(&mut self) {
72-
ffi::InitializeCriticalSection(self.inner.get());
169+
c::InitializeCriticalSection(self.inner.get());
73170
}
74171

75172
pub unsafe fn lock(&self) {
76-
ffi::EnterCriticalSection(self.inner.get());
173+
c::EnterCriticalSection(self.inner.get());
77174
}
78175

79176
#[inline]
80177
pub unsafe fn try_lock(&self) -> bool {
81-
ffi::TryEnterCriticalSection(self.inner.get()) != 0
178+
c::TryEnterCriticalSection(self.inner.get()) != 0
82179
}
83180

84181
pub unsafe fn unlock(&self) {
85-
ffi::LeaveCriticalSection(self.inner.get());
182+
c::LeaveCriticalSection(self.inner.get());
86183
}
87184

88185
pub unsafe fn destroy(&self) {
89-
ffi::DeleteCriticalSection(self.inner.get());
186+
c::DeleteCriticalSection(self.inner.get());
90187
}
91188
}

‎src/libstd/sys/windows/rwlock.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,40 @@
1111
use prelude::v1::*;
1212

1313
use cell::UnsafeCell;
14-
use sys::sync as ffi;
14+
use sys::c;
1515

16-
pub struct RWLock { inner: UnsafeCell<ffi::SRWLOCK> }
16+
pub struct RWLock { inner: UnsafeCell<c::SRWLOCK> }
1717

1818
unsafe impl Send for RWLock {}
1919
unsafe impl Sync for RWLock {}
2020

2121
impl RWLock {
2222
pub const fn new() -> RWLock {
23-
RWLock { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) }
23+
RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) }
2424
}
2525
#[inline]
2626
pub unsafe fn read(&self) {
27-
ffi::AcquireSRWLockShared(self.inner.get())
27+
c::AcquireSRWLockShared(self.inner.get())
2828
}
2929
#[inline]
3030
pub unsafe fn try_read(&self) -> bool {
31-
ffi::TryAcquireSRWLockShared(self.inner.get()) != 0
31+
c::TryAcquireSRWLockShared(self.inner.get()) != 0
3232
}
3333
#[inline]
3434
pub unsafe fn write(&self) {
35-
ffi::AcquireSRWLockExclusive(self.inner.get())
35+
c::AcquireSRWLockExclusive(self.inner.get())
3636
}
3737
#[inline]
3838
pub unsafe fn try_write(&self) -> bool {
39-
ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0
39+
c::TryAcquireSRWLockExclusive(self.inner.get()) != 0
4040
}
4141
#[inline]
4242
pub unsafe fn read_unlock(&self) {
43-
ffi::ReleaseSRWLockShared(self.inner.get())
43+
c::ReleaseSRWLockShared(self.inner.get())
4444
}
4545
#[inline]
4646
pub unsafe fn write_unlock(&self) {
47-
ffi::ReleaseSRWLockExclusive(self.inner.get())
47+
c::ReleaseSRWLockExclusive(self.inner.get())
4848
}
4949

5050
#[inline]

‎src/libstd/sys/windows/stack_overflow.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use rt::util::report_overflow;
1211
use core::prelude::*;
13-
use ptr;
14-
use mem;
12+
13+
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG};
1514
use libc;
16-
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
15+
use mem;
16+
use ptr;
17+
use rt::util::report_overflow;
18+
use sys::c;
1719
use sys_common::stack;
1820

1921
pub struct Handler {
@@ -69,8 +71,12 @@ pub unsafe fn cleanup() {
6971
}
7072

7173
pub unsafe fn make_handler() -> Handler {
72-
if SetThreadStackGuarantee(&mut 0x5000) == 0 {
73-
panic!("failed to reserve stack space for exception handling");
74+
// This API isn't available on XP, so don't panic in that case and just pray
75+
// it works out ok.
76+
if c::SetThreadStackGuarantee(&mut 0x5000) == 0 {
77+
if libc::GetLastError() as u32 != libc::ERROR_CALL_NOT_IMPLEMENTED as u32 {
78+
panic!("failed to reserve stack space for exception handling");
79+
}
7480
}
7581

7682
Handler { _data: 0 as *mut libc::c_void }
@@ -103,5 +109,4 @@ extern "system" {
103109
fn AddVectoredExceptionHandler(FirstHandler: ULONG,
104110
VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
105111
-> LPVOID;
106-
fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL;
107112
}

‎src/libstd/sys/windows/sync.rs

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.