Skip to content

Commit

Permalink
Add winapi srwlock to benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
nico-abram committed Oct 24, 2020
1 parent ab3718f commit 0d81897
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 10 deletions.
3 changes: 3 additions & 0 deletions benchmark/Cargo.toml
Expand Up @@ -20,3 +20,6 @@ path = "src/rwlock.rs"
[features]
nightly = ["parking_lot/nightly"]
deadlock_detection = ["parking_lot/deadlock_detection"]

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["synchapi"] }
60 changes: 59 additions & 1 deletion benchmark/src/mutex.rs
Expand Up @@ -8,7 +8,7 @@
mod args;
use crate::args::ArgRange;

#[cfg(unix)]
#[cfg(any(windows, unix))]
use std::cell::UnsafeCell;
use std::{
sync::{
Expand Down Expand Up @@ -57,6 +57,55 @@ impl<T> Mutex<T> for parking_lot::Mutex<T> {
}
}

#[cfg(not(windows))]
type SrwLock<T> = std::sync::Mutex<T>;

#[cfg(windows)]
use winapi::um::synchapi;
#[cfg(windows)]
struct SrwLock<T>(UnsafeCell<T>, UnsafeCell<synchapi::SRWLOCK>);
#[cfg(windows)]
unsafe impl<T> Sync for SrwLock<T> {}
#[cfg(windows)]
unsafe impl<T: Send> Send for SrwLock<T> {}
#[cfg(windows)]
impl<T> Mutex<T> for SrwLock<T> {
fn new(v: T) -> Self {
let mut h: synchapi::SRWLOCK = synchapi::SRWLOCK { Ptr: std::ptr::null_mut() };

unsafe {
synchapi::InitializeSRWLock(&mut h);
}
SrwLock(
UnsafeCell::new(v),
UnsafeCell::new(h),
)
}
fn lock<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
unsafe {
synchapi::AcquireSRWLockExclusive(self.1.get());
let res = f(&mut *self.0.get());
synchapi::ReleaseSRWLockExclusive(self.1.get());
res
}
}
fn name() -> &'static str {
"winapi_srwlock"
}
}
#[cfg(windows)]
impl<T> Drop for SrwLock<T> {
fn drop(&mut self) {
// TODO: Do we need to do anything here? CloseHandle?
// This makes me believe no: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
// An unlocked SRW lock with no waiting threads is in its initial state and can be copied, moved,
// and forgotten without being explicitly destroyed.
}
}

#[cfg(not(unix))]
type PthreadMutex<T> = std::sync::Mutex<T>;

Expand Down Expand Up @@ -220,6 +269,15 @@ fn run_all(
seconds_per_test,
test_iterations,
);
if cfg!(windows) {
run_benchmark_iterations::<SrwLock<f64>>(
num_threads,
work_per_critical_section,
work_between_critical_sections,
seconds_per_test,
test_iterations,
);
}
if cfg!(unix) {
run_benchmark_iterations::<PthreadMutex<f64>>(
num_threads,
Expand Down
80 changes: 71 additions & 9 deletions benchmark/src/rwlock.rs
Expand Up @@ -8,7 +8,7 @@
mod args;
use crate::args::ArgRange;

#[cfg(unix)]
#[cfg(any(windows, unix))]
use std::cell::UnsafeCell;
use std::{
sync::{
Expand Down Expand Up @@ -93,6 +93,66 @@ impl<T: Copy> RwLock<T> for seqlock::SeqLock<T> {
}
}

#[cfg(not(windows))]
type SrwLock<T> = std::sync::RwLock<T>;

#[cfg(windows)]
use winapi::um::synchapi;
#[cfg(windows)]
struct SrwLock<T>(UnsafeCell<T>, UnsafeCell<synchapi::SRWLOCK>);
#[cfg(windows)]
unsafe impl<T> Sync for SrwLock<T> {}
#[cfg(windows)]
unsafe impl<T: Send> Send for SrwLock<T> {}
#[cfg(windows)]
impl<T> RwLock<T> for SrwLock<T> {
fn new(v: T) -> Self {
let mut h: synchapi::SRWLOCK = synchapi::SRWLOCK { Ptr: std::ptr::null_mut() };

unsafe {
synchapi::InitializeSRWLock(&mut h);
}
SrwLock(
UnsafeCell::new(v),
UnsafeCell::new(h),
)
}
fn read<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
unsafe {
synchapi::AcquireSRWLockShared(self.1.get());
let res = f(&*self.0.get());
synchapi::ReleaseSRWLockShared(self.1.get());
res
}
}
fn write<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
unsafe {
synchapi::AcquireSRWLockExclusive(self.1.get());
let res = f(&mut *self.0.get());
synchapi::ReleaseSRWLockExclusive(self.1.get());
res
}
}
fn name() -> &'static str {
"winapi_srwlock"
}
}
#[cfg(windows)]
impl<T> Drop for SrwLock<T> {
fn drop(&mut self) {
// TODO: Do we need to do anything here? CloseHandle?
// This makes me believe no: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
// An unlocked SRW lock with no waiting threads is in its initial state and can be copied, moved,
// and forgotten without being explicitly destroyed.
}
}

#[cfg(not(unix))]
type PthreadRwLock<T> = std::sync::RwLock<T>;

Expand Down Expand Up @@ -304,14 +364,16 @@ fn run_all(
seconds_per_test,
test_iterations,
);
run_benchmark_iterations::<std::sync::RwLock<f64>>(
num_writer_threads,
num_reader_threads,
work_per_critical_section,
work_between_critical_sections,
seconds_per_test,
test_iterations,
);
if cfg!(windows) {
run_benchmark_iterations::<std::sync::RwLock<f64>>(
num_writer_threads,
num_reader_threads,
work_per_critical_section,
work_between_critical_sections,
seconds_per_test,
test_iterations,
);
}
if cfg!(unix) {
run_benchmark_iterations::<PthreadRwLock<f64>>(
num_writer_threads,
Expand Down

0 comments on commit 0d81897

Please sign in to comment.