Skip to content

Commit

Permalink
tests: Add further testing
Browse files Browse the repository at this point in the history
Co-authored-by: Jacob Rothstein <hi@jbr.me>
Signed-off-by: John Nunley <dev@notgull.net>
  • Loading branch information
notgull and jbr committed Apr 16, 2024
1 parent 6b73d69 commit f659391
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 28 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@ jobs:
- uses: rustsec/audit-check@master
with:
token: ${{ secrets.GITHUB_TOKEN }}

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ waker-fn = "1"
[[bench]]
name = "bench"
harness = false
required-features = ["std"]

[lib]
bench = false
29 changes: 15 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
//!
//! [`portable-atomic`]: https://crates.io/crates/portable-atomic

#![cfg_attr(coverage, feature(coverage_attribute))]
#![doc(
html_favicon_url = "https://github.com/raw/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
Expand All @@ -83,10 +84,6 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

use loom::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
use loom::Arc;
use notify::{Internal, NotificationPrivate};

use core::fmt;
use core::future::Future;
use core::mem::ManuallyDrop;
Expand All @@ -95,6 +92,10 @@ use core::pin::Pin;
use core::ptr;
use core::task::{Context, Poll};
use core::usize;
use notify::{Internal, NotificationPrivate};

use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::sync::Arc;

#[cfg(all(feature = "std", not(target_family = "wasm")))]
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -565,12 +566,11 @@ impl<T> Event<T> {
impl<T> Drop for Event<T> {
#[inline]
fn drop(&mut self) {
let inner: *mut Inner<T> = *self.inner.get_mut();

let inner = self.inner.get_mut();
// If the state pointer has been initialized, deallocate it.
if !inner.is_null() {
unsafe {
drop(Arc::from_raw(inner));
drop(Arc::from_raw(*inner));
}
}
}
Expand All @@ -588,9 +588,9 @@ impl fmt::Debug for Event {
}
}

impl Default for Event {
fn default() -> Event {
Event::new()
impl<T> Default for Event<T> {
fn default() -> Event<T> {
Event::with_tag()
}
}

Expand Down Expand Up @@ -808,16 +808,16 @@ fn full_fence() {
// The ideal solution here would be to use inline assembly, but we're instead creating a
// temporary atomic variable and compare-and-exchanging its value. No sane compiler to
// x86 platforms is going to optimize this away.
atomic::compiler_fence(Ordering::SeqCst);
sync::atomic::compiler_fence(Ordering::SeqCst);
let a = AtomicUsize::new(0);
let _ = a.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst);
atomic::compiler_fence(Ordering::SeqCst);
sync::atomic::compiler_fence(Ordering::SeqCst);
} else {
atomic::fence(Ordering::SeqCst);
sync::atomic::fence(Ordering::SeqCst);
}
}

mod loom {
pub(crate) mod sync {
#[cfg(not(feature = "portable-atomic"))]
pub(crate) use core::sync::atomic;

Expand All @@ -836,6 +836,7 @@ mod __sealed {
pub trait Sealed {}
}

#[test]
fn __test_send_and_sync() {
fn _assert_send<T: Send>() {}
fn _assert_sync<T: Sync>() {}
Expand Down
10 changes: 5 additions & 5 deletions src/linked_list/lock_free.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! Implementation of the linked list using lock-free primitives.

use crate::loom::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use crate::notify::{GenericNotify, Internal, Notification};

use core::cell::{Cell, UnsafeCell};
use core::cmp::Reverse;
use core::fmt;
Expand All @@ -18,6 +16,8 @@ use core::task::{Context, Poll, Waker};
use alloc::boxed::Box;
use alloc::collections::BinaryHeap;

use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};

/// The total number of buckets stored in each thread local.
/// All buckets combined can hold up to `usize::MAX - 1` entries.
const BUCKETS: usize = (usize::BITS - 1) as usize;
Expand Down Expand Up @@ -662,14 +662,14 @@ impl<T> Drop for Slots<T> {
fn drop(&mut self) {
// Free every bucket.
for (i, bucket) in self.buckets.iter_mut().enumerate() {
let bucket = *bucket.get_mut();
let bucket = bucket.get_mut();
if bucket.is_null() {
continue;
return;
}

// Drop the bucket.
let size = bucket_index_to_size(i);
drop(unsafe { Box::from_raw(slice::from_raw_parts_mut(bucket, size)) });
drop(unsafe { Box::from_raw(slice::from_raw_parts_mut(*bucket, size)) });
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions src/linked_list/mutex.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! Implementation of the linked list using standard library mutexes.

use crate::loom::atomic::{AtomicUsize, Ordering};
use crate::notify::{GenericNotify, Internal, Notification};

use crate::sync::atomic::{AtomicUsize, Ordering};
use std::boxed::Box;
use std::cell::{Cell, UnsafeCell};
use std::fmt;
Expand All @@ -13,7 +12,6 @@ use std::sync::{Mutex, MutexGuard, TryLockError};
use std::task::{Context, Poll, Waker};
use std::thread::{self, Thread};
use std::time::Instant;
use std::usize;

/// Inner state of [`Event`].
pub(crate) struct Inner<T> {
Expand All @@ -34,7 +32,7 @@ impl<T> Inner<T> {
pub(crate) fn new() -> Self {
Inner {
notified: AtomicUsize::new(usize::MAX),
list: std::sync::Mutex::new(List::<T> {
list: Mutex::new(List::<T> {
head: None,
tail: None,
start: None,
Expand Down Expand Up @@ -290,6 +288,7 @@ impl<T> Deref for ListGuard<'_, T> {
type Target = List<T>;

#[inline]
#[cfg_attr(coverage, coverage(off))]
fn deref(&self) -> &List<T> {
&self.guard
}
Expand Down Expand Up @@ -403,7 +402,7 @@ impl<T> List<T> {
entry.as_ref().state.replace(State::Created)
} else {
// Deallocate the entry.
Box::from_raw(entry.as_ptr()).state.into_inner()
Box::from_raw(entry.as_ptr()).state.replace(State::Created)
};

// Update the counters.
Expand Down
4 changes: 2 additions & 2 deletions src/notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#[cfg(feature = "std")]
use core::fmt;

use crate::loom::atomic::{self, Ordering};
use crate::sync::atomic::{self, Ordering};

pub(crate) use __private::Internal;

Expand Down Expand Up @@ -568,7 +568,7 @@ impl_for_numeric_types! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
/// Equivalent to `atomic::fence(Ordering::SeqCst)`, but in some cases faster.
#[inline]
pub(super) fn full_fence() {
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(miri), not(loom)))]
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(miri)))]
{
use core::{arch::asm, cell::UnsafeCell};
// HACK(stjepang): On x86 architectures there are two different ways of executing
Expand Down
65 changes: 64 additions & 1 deletion tests/notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
use std::task::Context;
use std::usize;

use event_listener::{Event, EventListener};
use event_listener::{Event, EventListener, Listener};
use waker_fn::waker_fn;

fn is_notified(listener: &mut EventListener) -> bool {
Expand All @@ -14,6 +14,20 @@ fn is_notified(listener: &mut EventListener) -> bool {
.is_ready()
}

#[test]
fn debug() {
let event = Event::new();
let fmt = format!("{:?}", &event);
assert!(fmt.contains("Event"));

let listener = event.listen();
let fmt = format!("{:?}", &listener);
assert!(fmt.contains("EventListener"));

let fmt = format!("{:?}", &event);
assert!(fmt.contains("Event"));
}

#[test]
fn notify() {
let event = Event::new();
Expand Down Expand Up @@ -192,6 +206,55 @@ fn drop_non_notified() {
assert!(!is_notified(&mut l2));
}

#[test]
fn discard() {
let event = Event::default();

let l1 = event.listen();
assert!(!l1.discard());

let l1 = event.listen();
event.notify(1);
assert!(l1.discard());

let l1 = event.listen();
event.notify_additional(1);
assert!(l1.discard());

let mut l1 = event.listen();
event.notify(1);
assert!(is_notified(&mut l1));
assert!(!l1.discard());
}

#[test]
fn same_event() {
let e1 = Event::new();
let e2 = Event::new();

let l1 = e1.listen();
let l2 = e1.listen();
let l3 = e2.listen();

assert!(l1.listens_to(&e1));
assert!(!l1.listens_to(&e2));
assert!(l1.same_event(&l2));
assert!(!l1.same_event(&l3));
}

#[test]
#[should_panic = "cannot poll a completed `EventListener` future"]
fn poll_twice() {
let event = Event::new();
let mut l1 = event.listen();
event.notify(1);

assert!(is_notified(&mut l1));

// Panic here.
is_notified(&mut l1);
}

#[test]
fn notify_all_fair() {
let event = Event::new();
Expand Down
87 changes: 86 additions & 1 deletion tests/park.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,36 @@

#![cfg(feature = "std")]

use event_listener::{Event, IntoNotification, Listener};
use event_listener::{Event, EventListener, IntoNotification, Listener};

use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::thread;
use std::time::{Duration, Instant};

use waker_fn::waker_fn;

fn is_notified(listener: &mut EventListener) -> bool {
let waker = waker_fn(|| ());
Pin::new(listener)
.poll(&mut Context::from_waker(&waker))
.is_ready()
}

#[test]
fn total_listeners() {
let event = Event::new();
assert_eq!(event.total_listeners(), 0);

let listener = event.listen();
assert_eq!(event.total_listeners(), 1);

drop(listener);
assert_eq!(event.total_listeners(), 0);
}

#[test]
fn wait() {
let event = Event::new();
Expand Down Expand Up @@ -32,6 +59,18 @@ fn wait_timeout() {
assert_eq!(listener.wait_timeout(Duration::from_millis(50)), Some(()));
}

#[test]
fn wait_deadline() {
let event = Event::new();
let listener = event.listen();

assert_eq!(event.notify(1), 1);
assert_eq!(
listener.wait_deadline(Instant::now() + Duration::from_millis(50)),
Some(())
);
}

#[test]
fn wait_timeout_expiry() {
let event = Event::new();
Expand All @@ -41,3 +80,49 @@ fn wait_timeout_expiry() {
assert_eq!(listener.wait_timeout(Duration::from_millis(200)), None);
assert!(Instant::now().duration_since(start) >= Duration::from_millis(200));
}

#[test]
fn unpark() {
let event = Arc::new(Event::new());
let listener = event.listen();

thread::spawn({
let event = event.clone();
move || {
thread::sleep(Duration::from_millis(100));
event.notify(1);
}
});

listener.wait();
}

#[test]
fn unpark_timeout() {
let event = Arc::new(Event::new());
let listener = event.listen();

thread::spawn({
let event = event.clone();
move || {
thread::sleep(Duration::from_millis(100));
event.notify(1);
}
});

let x = listener.wait_timeout(Duration::from_millis(200));
assert!(x.is_some());
}

#[test]
#[should_panic = "cannot wait twice on an `EventListener`"]
fn wait_twice() {
let event = Event::new();
let mut listener = event.listen();
event.notify(1);

assert!(is_notified(&mut listener));

// Panic here.
listener.wait();
}

0 comments on commit f659391

Please sign in to comment.