Skip to content

Commit

Permalink
Add downlevel implementations for Mutex, CondVar and RwLock
Browse files Browse the repository at this point in the history
Because of the movability and const-initialization requirements, the
implementation uses LazyBox.

OnceLock cannot be used here as it needs a thread parker. The
windows-specific thread parker is XP+ only, and the generic thread
parker is implemented in tems of a condvar, which would make this a
circular dependency.

- Fall back to critical sections on NT4+, and `CreateMutex` objects for everything else. OnceLock is used to do the necessary initialization for these fallback implementations.

- Implement `RwLock` fallback by dropping down to a `Mutex`.

- Implement `CondVar` fallback based on `CreateEventA`/`PulseEvent`.
  This should be used with extreme caution as it may cause deadlocks.
  See which may cause deadlocks. See [Old New
  Thing](https://devblogs.microsoft.com/oldnewthing/20050105-00/?p=36803)
  and the [MSDN
  docs](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-pulseevent)
  for more information.
  • Loading branch information
seritools committed Dec 29, 2023
1 parent 643bd55 commit 08798a9
Show file tree
Hide file tree
Showing 12 changed files with 660 additions and 45 deletions.
30 changes: 29 additions & 1 deletion library/std/src/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ pub type LPWCH = *mut WCHAR;
pub type LPWSTR = *mut WCHAR;

pub type PLARGE_INTEGER = *mut c_longlong;
pub type PSRWLOCK = *mut SRWLOCK;

pub type socklen_t = c_int;
pub type ADDRESS_FAMILY = USHORT;
Expand Down Expand Up @@ -355,6 +354,35 @@ compat_fn_optional! {
pub fn WakeByAddressSingle(address: *const ::core::ffi::c_void);
}

compat_fn_optional! {
crate::sys::compat::load_try_enter_critical_section_function();
// >= NT 4
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryentercriticalsection
pub fn TryEnterCriticalSection(lpcriticalsection: *mut CRITICAL_SECTION) -> BOOL;
}

compat_fn_optional! {
crate::sys::compat::load_srw_functions();
// >= Win7 / Server 2008 R2
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockexclusive
pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN;
pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN;
// >= Vista / Server 2008
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-acquiresrwlockexclusive
pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> ();
pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK) -> ();
pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> ();
pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK) -> ();
pub fn SleepConditionVariableSRW(
conditionvariable: *mut CONDITION_VARIABLE,
srwlock: *mut SRWLOCK,
dwmilliseconds: u32,
flags: u32,
) -> BOOL;
pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> ();
pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> ();
}

compat_fn_with_fallback! {
pub static NTDLL: &CStr = c"ntdll" => { load: true, unicows: false };

Expand Down
13 changes: 13 additions & 0 deletions library/std/src/sys/windows/c/windows_sys.lst
Original file line number Diff line number Diff line change
Expand Up @@ -2595,3 +2595,16 @@ Windows.Win32.UI.Shell.GetUserProfileDirectoryW
// tidy-alphabetical-end

Windows.Win32.System.LibraryLoader.LoadLibraryA

// sync primitives
Windows.Win32.System.Threading.CreateMutexA
Windows.Win32.System.Threading.ReleaseMutex
Windows.Win32.System.Threading.CreateEventA
Windows.Win32.System.Threading.PulseEvent
Windows.Win32.System.Threading.CRITICAL_SECTION
Windows.Win32.System.Threading.InitializeCriticalSection
Windows.Win32.System.Threading.EnterCriticalSection
Windows.Win32.System.Threading.LeaveCriticalSection
Windows.Win32.System.Threading.DeleteCriticalSection
Windows.Win32.System.Threading.SRWLOCK
Windows.Win32.System.Threading.CONDITION_VARIABLE
85 changes: 85 additions & 0 deletions library/std/src/sys/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn CreateEventA(
lpeventattributes: *const SECURITY_ATTRIBUTES,
bmanualreset: BOOL,
binitialstate: BOOL,
lpname: PCSTR,
) -> HANDLE;
}
#[link(name = "kernel32")]
extern "system" {
pub fn CreateEventW(
lpeventattributes: *const SECURITY_ATTRIBUTES,
Expand Down Expand Up @@ -104,6 +113,14 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn CreateMutexA(
lpmutexattributes: *const SECURITY_ATTRIBUTES,
binitialowner: BOOL,
lpname: PCSTR,
) -> HANDLE;
}
#[link(name = "kernel32")]
extern "system" {
pub fn CreateNamedPipeW(
lpname: PCWSTR,
Expand Down Expand Up @@ -160,6 +177,10 @@ extern "system" {
) -> HANDLE;
}
#[link(name = "kernel32")]
extern "system" {
pub fn DeleteCriticalSection(lpcriticalsection: *mut CRITICAL_SECTION) -> ();
}
#[link(name = "kernel32")]
extern "system" {
pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL;
}
Expand Down Expand Up @@ -193,6 +214,10 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn EnterCriticalSection(lpcriticalsection: *mut CRITICAL_SECTION) -> ();
}
#[link(name = "kernel32")]
extern "system" {
pub fn ExitProcess(uexitcode: u32) -> !;
}
Expand Down Expand Up @@ -385,6 +410,10 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn InitializeCriticalSection(lpcriticalsection: *mut CRITICAL_SECTION) -> ();
}
#[link(name = "kernel32")]
extern "system" {
pub fn InitializeProcThreadAttributeList(
lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST,
Expand All @@ -394,6 +423,10 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn LeaveCriticalSection(lpcriticalsection: *mut CRITICAL_SECTION) -> ();
}
#[link(name = "kernel32")]
extern "system" {
pub fn LoadLibraryA(lplibfilename: PCSTR) -> HMODULE;
}
Expand All @@ -417,6 +450,10 @@ extern "system" {
) -> i32;
}
#[link(name = "kernel32")]
extern "system" {
pub fn PulseEvent(hevent: HANDLE) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn QueryPerformanceCounter(lpperformancecount: *mut i64) -> BOOL;
}
Expand Down Expand Up @@ -455,6 +492,10 @@ extern "system" {
) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn ReleaseMutex(hmutex: HANDLE) -> BOOL;
}
#[link(name = "kernel32")]
extern "system" {
pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> ();
}
Expand Down Expand Up @@ -1191,6 +1232,39 @@ pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32;
pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32;
pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32;
pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32;
#[repr(C)]
pub struct CRITICAL_SECTION {
pub DebugInfo: *mut CRITICAL_SECTION_DEBUG,
pub LockCount: i32,
pub RecursionCount: i32,
pub OwningThread: HANDLE,
pub LockSemaphore: HANDLE,
pub SpinCount: usize,
}
impl ::core::marker::Copy for CRITICAL_SECTION {}
impl ::core::clone::Clone for CRITICAL_SECTION {
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
pub struct CRITICAL_SECTION_DEBUG {
pub Type: u16,
pub CreatorBackTraceIndex: u16,
pub CriticalSection: *mut CRITICAL_SECTION,
pub ProcessLocksList: LIST_ENTRY,
pub EntryCount: u32,
pub ContentionCount: u32,
pub Flags: u32,
pub CreatorBackTraceIndexHigh: u16,
pub Identifier: u16,
}
impl ::core::marker::Copy for CRITICAL_SECTION_DEBUG {}
impl ::core::clone::Clone for CRITICAL_SECTION_DEBUG {
fn clone(&self) -> Self {
*self
}
}
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32;
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32;
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32;
Expand Down Expand Up @@ -3653,6 +3727,17 @@ impl ::core::clone::Clone for LINGER {
*self
}
}
#[repr(C)]
pub struct LIST_ENTRY {
pub Flink: *mut LIST_ENTRY,
pub Blink: *mut LIST_ENTRY,
}
impl ::core::marker::Copy for LIST_ENTRY {}
impl ::core::clone::Clone for LIST_ENTRY {
fn clone(&self) -> Self {
*self
}
}
pub type LPOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option<
unsafe extern "system" fn(
dwerrorcode: u32,
Expand Down
48 changes: 48 additions & 0 deletions library/std/src/sys/windows/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ unsafe extern "C" fn init() {
// because this function runs during global initialization. For example, DO NOT
// do any dynamic allocation, don't call LoadLibrary, etc.

// check all the different synchronization primitives ...
load_try_enter_critical_section_function();
load_srw_functions();
// ... and init mutex downlevel compat based on it
super::locks::compat::init();

// Attempt to preload the synch functions.
load_synch_functions();
#[cfg(not(target_vendor = "uwp"))]
Expand Down Expand Up @@ -405,3 +411,45 @@ pub(super) fn load_stack_overflow_functions() {

try_load();
}

pub(super) fn load_try_enter_critical_section_function() {
fn try_load() -> Option<()> {
const MODULE_NAME: &CStr = c"kernel32";

let library = unsafe { Module::new(MODULE_NAME) }?;
static_load!(library, [TryEnterCriticalSection]);
Some(())
}

try_load();
}

pub(super) fn load_srw_functions() {
fn try_load() -> Option<()> {
const MODULE_NAME: &CStr = c"kernel32";

// Try loading the library and all the required functions.
// If any step fails, then they all fail.
let library = unsafe { Module::new(MODULE_NAME) }?;

static_load!(
library,
[
// check the try_ functions first, as they have higher system requirements
TryAcquireSRWLockExclusive,
TryAcquireSRWLockShared,
AcquireSRWLockExclusive,
AcquireSRWLockShared,
ReleaseSRWLockExclusive,
ReleaseSRWLockShared,
SleepConditionVariableSRW,
WakeAllConditionVariable,
WakeConditionVariable
]
);

Some(())
}

try_load();
}
Loading

0 comments on commit 08798a9

Please sign in to comment.