Skip to content

Commit

Permalink
Add more tests. (#7)
Browse files Browse the repository at this point in the history
Adapt libstd's documentation-comment tests into testcases.
  • Loading branch information
sunfishcode authored Dec 4, 2023
1 parent 6a542d7 commit cf932bf
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 0 deletions.
188 changes: 188 additions & 0 deletions tests/mutex-examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//! The following is derived from the documentation tests in Rust's
//! library/std/src/sync/mutex.rs at revision
//! b7e68dfc945c4cf8d1753cc3a8be48a71bb39d5a.
#[test]
fn mutex_example_0() {
use std::sync::Arc;
use rustix_futex_sync::Mutex;
use std::thread;
use std::sync::mpsc::channel;

const N: usize = 10;

// Spawn a few threads to increment a shared variable (non-atomically), and
// let the main thread know once all increments are done.
//
// Here we're using an Arc to share memory among threads, and the data inside
// the Arc is protected with a mutex.
let data = Arc::new(Mutex::new(0));

let (tx, rx) = channel();
for _ in 0..N {
let (data, tx) = (Arc::clone(&data), tx.clone());
thread::spawn(move || {
// The shared state can only be accessed once the lock is held.
// Our non-atomic increment is safe because we're the only thread
// which can access the shared state when the lock is held.
//
// We unwrap() the return value to assert that we are not expecting
// threads to ever fail while holding the lock.
let mut data = data.lock();
*data += 1;
if *data == N {
tx.send(()).unwrap();
}
// the lock is unlocked here when `data` goes out of scope.
});
}

rx.recv().unwrap();
}

#[test]
fn mutex_example_1() {
use std::sync::Arc;
use rustix_futex_sync::Mutex;
use std::thread;

const N: usize = 3;

let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
let res_mutex = Arc::new(Mutex::new(0));

let mut threads = Vec::with_capacity(N);
(0..N).for_each(|_| {
let data_mutex_clone = Arc::clone(&data_mutex);
let res_mutex_clone = Arc::clone(&res_mutex);

threads.push(thread::spawn(move || {
// Here we use a block to limit the lifetime of the lock guard.
let result = {
let mut data = data_mutex_clone.lock();
// This is the result of some important and long-ish work.
let result = data.iter().fold(0, |acc, x| acc + x * 2);
data.push(result);
result
// The mutex guard gets dropped here, together with any other values
// created in the critical section.
};
// The guard created here is a temporary dropped at the end of the statement, i.e.
// the lock would not remain being held even if the thread did some additional work.
*res_mutex_clone.lock() += result;
}));
});

let mut data = data_mutex.lock();
// This is the result of some important and long-ish work.
let result = data.iter().fold(0, |acc, x| acc + x * 2);
data.push(result);
// We drop the `data` explicitly because it's not necessary anymore and the
// thread still has work to do. This allow other threads to start working on
// the data immediately, without waiting for the rest of the unrelated work
// to be done here.
//
// It's even more important here than in the threads because we `.join` the
// threads after that. If we had not dropped the mutex guard, a thread could
// be waiting forever for it, causing a deadlock.
// As in the threads, a block could have been used instead of calling the
// `drop` function.
drop(data);
// Here the mutex guard is not assigned to a variable and so, even if the
// scope does not end after this line, the mutex is still released: there is
// no deadlock.
*res_mutex.lock() += result;

threads.into_iter().for_each(|thread| {
thread
.join()
.expect("The thread creating or execution failed !")
});

assert_eq!(*res_mutex.lock(), 800);
}

#[test]
fn mutex_example_2() {
use rustix_futex_sync::Mutex;

let _mutex = Mutex::new(0);
}

#[test]
fn mutex_example_3() {
use std::sync::Arc;
use rustix_futex_sync::Mutex;
use std::thread;

let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);

thread::spawn(move || {
*c_mutex.lock() = 10;
}).join().expect("thread::spawn failed");
assert_eq!(*mutex.lock(), 10);
}

#[test]
fn mutex_example_4() {
use std::sync::Arc;
use rustix_futex_sync::Mutex;
use std::thread;

let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);

thread::spawn(move || {
let mut lock = c_mutex.try_lock();
if let Some(ref mut mutex) = lock {
**mutex = 10;
} else {
println!("try_lock failed");
}
}).join().expect("thread::spawn failed");
assert_eq!(*mutex.lock(), 10);
}

#[test]
fn mutex_example_5() {
use rustix_futex_sync::Mutex;
let mutex = Mutex::new(0);

let mut guard = mutex.lock();
*guard += 20;
//Mutex::unlock(guard);
}

#[test]
fn mutex_example_6() {
use std::sync::Arc;
use rustix_futex_sync::Mutex;
use std::thread;

let mutex = Arc::new(Mutex::new(0));
let c_mutex = Arc::clone(&mutex);

let _ = thread::spawn(move || {
let _lock = c_mutex.lock();
panic!(); // the mutex gets poisoned
}).join();
//assert_eq!(mutex.is_poisoned(), true);
}

#[test]
fn mutex_example_7() {
use rustix_futex_sync::Mutex;

let mutex = Mutex::new(0);
assert_eq!(mutex.into_inner(), 0);
}

#[test]
fn mutex_example_8() {
use rustix_futex_sync::Mutex;

let mut mutex = Mutex::new(0);
*mutex.get_mut() = 10;
assert_eq!(*mutex.lock(), 10);
}
22 changes: 22 additions & 0 deletions tests/parking_lot-issue_392.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! This test is derived from `parking_lot` at
//! <https://github.com/Amanieu/parking_lot/blob/master/tests/issue_392.rs>
//! at revision 64b711461ed528de7c05868e5024f21e602cd4e0.
// rustix_futex_sync doesn't currently support upgrading
/*
use rustix_futex_sync::RwLock;
struct Lock(RwLock<i32>);
#[test]
fn issue_392() {
let lock = Lock(RwLock::new(0));
let mut rl = lock.0.upgradable_read();
rl.with_upgraded(|_| {
println!("lock upgrade");
});
rl.with_upgraded(|_| {
println!("lock upgrade");
});
}
*/
122 changes: 122 additions & 0 deletions tests/rwlock-examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! The following is derived from the documentation tests in Rust's
//! library/std/src/sync/rwlock.rs at revision
//! cdf25c904e7dc6eee089e1ccebc5fbd05351dfb9.
#[test]
fn rwlock_example_0() {
use rustix_futex_sync::RwLock;

let lock = RwLock::new(5);

// many reader locks can be held at once
{
let r1 = lock.read();
let r2 = lock.read();
assert_eq!(*r1, 5);
assert_eq!(*r2, 5);
} // read locks are dropped at this point

// only one write lock may be held, however
{
let mut w = lock.write();
*w += 1;
assert_eq!(*w, 6);
} // write lock is dropped here
}

#[test]
fn rwlock_example_1() {
use rustix_futex_sync::RwLock;

let _lock = RwLock::new(5);
}

#[test]
fn rwlock_example_2() {
use std::sync::Arc;
use rustix_futex_sync::RwLock;
use std::thread;

let lock = Arc::new(RwLock::new(1));
let c_lock = Arc::clone(&lock);

let n = lock.read();
assert_eq!(*n, 1);

thread::spawn(move || {
let _r = c_lock.read();
}).join().unwrap();
}

#[test]
fn rwlock_example_3() {
use rustix_futex_sync::RwLock;

let lock = RwLock::new(1);

match lock.try_read() {
Some(n) => assert_eq!(*n, 1),
None => unreachable!(),
};
}

#[test]
fn rwlock_example_4() {
use rustix_futex_sync::RwLock;

let lock = RwLock::new(1);

let mut n = lock.write();
*n = 2;

assert!(lock.try_read().is_none());
}

#[test]
fn rwlock_example_5() {
use rustix_futex_sync::RwLock;

let lock = RwLock::new(1);

let n = lock.read();
assert_eq!(*n, 1);

assert!(lock.try_write().is_none());
}

#[test]
fn rwlock_example_6() {
use std::sync::Arc;
use rustix_futex_sync::RwLock;
use std::thread;

let lock = Arc::new(RwLock::new(0));
let c_lock = Arc::clone(&lock);

let _ = thread::spawn(move || {
let _lock = c_lock.write();
panic!(); // the lock gets poisoned
}).join();
//assert_eq!(lock.is_poisoned(), true);
}

#[test]
fn rwlock_example_7() {
use rustix_futex_sync::RwLock;

let lock = RwLock::new(String::new());
{
let mut s = lock.write();
*s = "modified".to_owned();
}
assert_eq!(lock.into_inner(), "modified");
}

#[test]
fn rwlock_example_8() {
use rustix_futex_sync::RwLock;

let mut lock = RwLock::new(0);
*lock.get_mut() = 10;
assert_eq!(*lock.read(), 10);
}

0 comments on commit cf932bf

Please sign in to comment.