extern crate alloc;
use alloc::{boxed::Box, sync};
use crate::{
alloc::SyncVec,
sync_types::{self, Lock as _},
};
use core::{cell, convert, future, marker, ops, pin, sync::atomic, task};
pub struct TestNopLock<T: marker::Send> {
locked: atomic::AtomicBool,
v: cell::UnsafeCell<T>,
}
impl<T: marker::Send> convert::From<T> for TestNopLock<T> {
fn from(value: T) -> Self {
Self {
locked: atomic::AtomicBool::new(false),
v: cell::UnsafeCell::new(value),
}
}
}
unsafe impl<T: marker::Send> marker::Send for TestNopLock<T> {}
unsafe impl<T: marker::Send> marker::Sync for TestNopLock<T> {}
impl<T: marker::Send> sync_types::Lock<T> for TestNopLock<T> {
type Guard<'a>
= TestNopLockGuard<'a, T>
where
Self: 'a;
fn lock(&self) -> Self::Guard<'_> {
assert_eq!(
self.locked
.compare_exchange(false, true, atomic::Ordering::Acquire, atomic::Ordering::Relaxed),
Ok(false),
"Testing TestNopLocks are not expected to ever be contended."
);
TestNopLockGuard { lock: self }
}
}
impl<T: marker::Send> sync_types::ConstructibleLock<T> for TestNopLock<T> {
fn get_mut(&mut self) -> &mut T {
assert!(!self.locked.load(atomic::Ordering::Relaxed));
let p = self.v.get();
unsafe { &mut *p }
}
}
pub struct TestNopLockGuard<'a, T: marker::Send> {
lock: &'a TestNopLock<T>,
}
impl<'a, T: marker::Send> Drop for TestNopLockGuard<'a, T> {
fn drop(&mut self) {
assert_eq!(
self.lock
.locked
.compare_exchange(true, false, atomic::Ordering::Acquire, atomic::Ordering::Relaxed),
Ok(true),
"Testing TestNopLock with active lock guard found unlocked."
);
}
}
impl<'a, T: marker::Send> ops::Deref for TestNopLockGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let p = self.lock.v.get();
unsafe { &*p }
}
}
impl<'a, T: marker::Send> ops::DerefMut for TestNopLockGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
let p = self.lock.v.get();
unsafe { &mut *p }
}
}
pub struct TestNopRwLock<T: marker::Send + marker::Sync> {
locked: atomic::AtomicIsize,
v: cell::UnsafeCell<T>,
}
impl<T: marker::Send + marker::Sync> convert::From<T> for TestNopRwLock<T> {
fn from(value: T) -> Self {
Self {
locked: atomic::AtomicIsize::new(0),
v: cell::UnsafeCell::new(value),
}
}
}
unsafe impl<T: marker::Send + marker::Sync> marker::Send for TestNopRwLock<T> {}
unsafe impl<T: marker::Send + marker::Sync> marker::Sync for TestNopRwLock<T> {}
impl<T: marker::Send + marker::Sync> sync_types::RwLock<T> for TestNopRwLock<T> {
type ReadGuard<'a>
= TestNopRwLockReadGuard<'a, T>
where
Self: 'a;
type WriteGuard<'a>
= TestNopRwLockWriteGuard<'a, T>
where
Self: 'a;
fn read(&self) -> Self::ReadGuard<'_> {
assert!(
self.locked.fetch_add(1, atomic::Ordering::Acquire) >= 0,
"Testing TestNopRwLocks are not expected to ever be contended."
);
TestNopRwLockReadGuard { lock: self }
}
fn write(&self) -> Self::WriteGuard<'_> {
assert_eq!(
self.locked.fetch_sub(1, atomic::Ordering::Acquire),
0,
"Testing TestNopRwLocks are not expected to ever be contended."
);
TestNopRwLockWriteGuard { lock: self }
}
fn get_mut(&mut self) -> &mut T {
assert_eq!(self.locked.load(atomic::Ordering::Relaxed), 0);
let p = self.v.get();
unsafe { &mut *p }
}
}
pub struct TestNopRwLockReadGuard<'a, T: marker::Send + marker::Sync> {
lock: &'a TestNopRwLock<T>,
}
impl<'a, T: marker::Send + marker::Sync> Drop for TestNopRwLockReadGuard<'a, T> {
fn drop(&mut self) {
assert!(
self.lock.locked.fetch_sub(1, atomic::Ordering::Release) > 0,
"Testing TestNopRwLock with active read guard found unlocked or write locked."
);
}
}
impl<'a, T: marker::Send + marker::Sync> ops::Deref for TestNopRwLockReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let p = self.lock.v.get();
unsafe { &*p }
}
}
pub struct TestNopRwLockWriteGuard<'a, T: marker::Send + marker::Sync> {
lock: &'a TestNopRwLock<T>,
}
impl<'a, T: marker::Send + marker::Sync> Drop for TestNopRwLockWriteGuard<'a, T> {
fn drop(&mut self) {
assert_eq!(
self.lock.locked.fetch_add(1, atomic::Ordering::Release),
-1,
"Testing TestNopRwLock with active lock write guard found unlocked or read locked."
);
}
}
impl<'a, T: marker::Send + marker::Sync> ops::Deref for TestNopRwLockWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let p = self.lock.v.get();
unsafe { &*p }
}
}
impl<'a, T: marker::Send + marker::Sync> ops::DerefMut for TestNopRwLockWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
let p = self.lock.v.get();
unsafe { &mut *p }
}
}
pub struct TestNopSyncTypes;
impl sync_types::SyncTypes for TestNopSyncTypes {
type Lock<T: marker::Send> = TestNopLock<T>;
type RwLock<T: marker::Send + marker::Sync> = TestNopRwLock<T>;
type SyncRcPtrFactory = sync_types::GenericArcFactory;
}
trait QueuedTaskDispatch: marker::Send {
fn poll_pinned(&mut self, cx: &mut task::Context<'_>) -> bool;
}
struct QueuedTask<F: future::Future + Send>
where
F::Output: Send + 'static,
{
f: F,
result: sync_types::GenericArc<TestNopLock<Option<F::Output>>>,
}
impl<F: future::Future + Send> QueuedTaskDispatch for QueuedTask<F>
where
F::Output: Send + 'static,
{
fn poll_pinned(&mut self, cx: &mut task::Context<'_>) -> bool {
let f = unsafe { pin::Pin::new_unchecked(&mut self.f) };
match future::Future::poll(f, cx) {
task::Poll::Ready(result) => {
*self.result.lock() = Some(result);
true
}
task::Poll::Pending => false,
}
}
}
enum TaskStatus {
Blocked,
Runnable,
}
struct TaskQueueEntry {
id: u64,
status: TaskStatus,
task: Option<pin::Pin<Box<dyn QueuedTaskDispatch>>>,
waiter_waker: Option<task::Waker>,
}
struct Waker {
task_id: u64,
executor: sync_types::GenericArc<TestAsyncExecutor>,
}
impl alloc::task::Wake for Waker {
fn wake(self: sync::Arc<Self>) {
let executor = &self.executor;
let mut tasks = executor.tasks.lock();
for t in tasks.iter_mut() {
if t.id == self.task_id && matches!(t.status, TaskStatus::Blocked) {
t.status = TaskStatus::Runnable
}
}
}
}
enum TaskWaiterState<T: marker::Send> {
Pending {
executor: sync_types::GenericArc<TestAsyncExecutor>,
task_id: u64,
result: sync_types::GenericArc<TestNopLock<Option<T>>>,
},
Done,
}
pub struct TestAsyncExecutorTaskWaiter<T: marker::Send> {
state: TaskWaiterState<T>,
}
impl<T: marker::Send> TestAsyncExecutorTaskWaiter<T> {
pub fn take(mut self) -> Option<T> {
match &mut self.state {
TaskWaiterState::Pending {
executor: _,
task_id: _,
result,
} => {
let result = result.lock().take();
self.state = TaskWaiterState::Done;
result
}
TaskWaiterState::Done => None,
}
}
}
impl<T: marker::Send> Drop for TestAsyncExecutorTaskWaiter<T> {
fn drop(&mut self) {
match &self.state {
TaskWaiterState::Pending {
executor,
task_id,
result,
} => {
if !result.lock().is_some() {
executor.remove_task(*task_id);
}
}
TaskWaiterState::Done => (),
}
}
}
impl<T: marker::Send> Unpin for TestAsyncExecutorTaskWaiter<T> {}
impl<T: marker::Send> future::Future for TestAsyncExecutorTaskWaiter<T> {
type Output = T;
fn poll(self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let this = self.get_mut();
match &this.state {
TaskWaiterState::Pending {
executor,
task_id,
result,
} => {
let mut locked_result = result.lock();
if let Some(result) = locked_result.take() {
drop(locked_result);
this.state = TaskWaiterState::Done;
task::Poll::Ready(result)
} else {
let mut tasks = executor.tasks.lock();
let task = tasks.iter_mut().find(|task| task.id == *task_id).unwrap();
task.waiter_waker = Some(cx.waker().clone());
task::Poll::Pending
}
}
TaskWaiterState::Done => unreachable!(),
}
}
}
pub struct TestAsyncExecutor {
tasks: TestNopLock<SyncVec<TaskQueueEntry>>,
next_id: atomic::AtomicU64,
}
impl TestAsyncExecutor {
pub fn new() -> sync_types::GenericArc<Self> {
<sync_types::GenericArcFactory as sync_types::SyncRcPtrFactory>::try_new(Self {
tasks: TestNopLock::from(SyncVec::new()),
next_id: atomic::AtomicU64::new(0),
})
.unwrap()
}
pub fn spawn<F: future::Future + Send + 'static>(
this: &sync_types::GenericArc<Self>,
f: F,
) -> TestAsyncExecutorTaskWaiter<F::Output>
where
F::Output: Send + 'static,
{
let id = this.next_id.fetch_add(1, atomic::Ordering::Relaxed);
let result =
<sync_types::GenericArcFactory as sync_types::SyncRcPtrFactory>::try_new(TestNopLock::from(None)).unwrap();
let waiter = TestAsyncExecutorTaskWaiter {
state: TaskWaiterState::Pending {
executor: this.clone(),
task_id: id,
result: result.clone(),
},
};
let task = Box::pin(QueuedTask { f, result }) as pin::Pin<Box<dyn QueuedTaskDispatch>>;
let tasks = this.tasks.lock();
let (mut tasks, r) = SyncVec::try_reserve_exact(&this.tasks, tasks, 1);
r.unwrap();
tasks.push(TaskQueueEntry {
id,
status: TaskStatus::Runnable,
task: Some(task),
waiter_waker: None,
});
waiter
}
fn remove_task(&self, id: u64) {
let mut tasks = self.tasks.lock();
if let Some(index) = tasks.iter().position(|task| task.id == id) {
let entry = tasks.remove(index);
drop(tasks);
drop(entry);
};
}
pub fn run_to_completion(this: &sync_types::GenericArc<Self>) {
let mut last_polled: Option<(usize, u64)> = None;
loop {
let mut tasks = this.tasks.lock();
if tasks.is_empty() {
break;
}
let mut search_begin = match last_polled {
Some((last_index, last_task_id)) => {
let last_index = last_index.min(tasks.len());
let last_before_leq = tasks[..last_index]
.iter()
.rposition(|entry| entry.id <= last_task_id)
.unwrap_or(0);
match tasks
.iter()
.enumerate()
.skip(last_before_leq)
.find(|(_, entry)| entry.id > last_task_id)
{
Some((index, _)) => index,
None => {
0
}
}
}
None => 0,
};
let index = loop {
match tasks
.iter()
.enumerate()
.skip(search_begin)
.find(|(_, entry)| matches!(entry.status, TaskStatus::Runnable))
{
Some((index, _)) => break Some(index),
None => {
if search_begin == 0 {
break None;
}
search_begin = 0;
}
}
};
let index = index.expect("TestAsyncExecutor stuck with no runnable task.");
let entry = &mut tasks[index];
let task_id = entry.id;
last_polled = Some((index, task_id));
let mut task = match entry.task.take() {
Some(task) => task,
None => {
continue;
}
};
entry.status = TaskStatus::Blocked;
drop(tasks);
let waker = task::Waker::from(sync::Arc::new(Waker {
task_id,
executor: this.clone(),
}));
let mut cx = task::Context::from_waker(&waker);
let done = unsafe { task.as_mut().get_unchecked_mut() }.poll_pinned(&mut cx);
let task = if done {
drop(task);
None
} else {
Some(task)
};
let mut tasks = this.tasks.lock();
let updated_index = if index < tasks.len() && tasks[index].id == task_id {
index
} else {
match tasks.iter().position(|entry| entry.id == task_id) {
Some(updated_index) => updated_index,
None => {
continue;
}
}
};
last_polled = Some((updated_index, task_id));
if done {
let waiter_waker = tasks[updated_index].waiter_waker.take();
tasks.remove(updated_index);
if let Some(waiter_waker) = waiter_waker {
drop(tasks);
waiter_waker.wake();
}
} else {
let entry = &mut tasks[updated_index];
entry.task = task;
}
}
}
}
#[test]
fn test_test_async_executor_simple() {
struct SimpleTask {}
impl future::Future for SimpleTask {
type Output = u32;
fn poll(self: pin::Pin<&mut Self>, _cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
task::Poll::Ready(42)
}
}
let executor = TestAsyncExecutor::new();
let waiter = TestAsyncExecutor::spawn(&executor, SimpleTask {});
TestAsyncExecutor::run_to_completion(&executor);
assert_eq!(waiter.take().unwrap(), 42);
assert_eq!(sync_types::GenericArc::strong_count(&executor), 1);
assert_eq!(sync_types::GenericArc::weak_count(&executor), 0);
let waiter = TestAsyncExecutor::spawn(&executor, async { async { 42 }.await });
TestAsyncExecutor::run_to_completion(&executor);
assert_eq!(waiter.take().unwrap(), 42);
assert_eq!(sync_types::GenericArc::strong_count(&executor), 1);
assert_eq!(sync_types::GenericArc::weak_count(&executor), 0);
}
#[test]
fn test_test_async_executor_chained_waiters() {
struct SimpleTask {}
impl future::Future for SimpleTask {
type Output = u32;
fn poll(self: pin::Pin<&mut Self>, _cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
task::Poll::Ready(42)
}
}
let executor = TestAsyncExecutor::new();
let waiter = TestAsyncExecutor::spawn(&executor, SimpleTask {});
let waiter = TestAsyncExecutor::spawn(&executor, waiter);
let waiter = TestAsyncExecutor::spawn(&executor, waiter);
TestAsyncExecutor::run_to_completion(&executor);
assert_eq!(waiter.take().unwrap(), 42);
assert_eq!(sync_types::GenericArc::strong_count(&executor), 1);
assert_eq!(sync_types::GenericArc::weak_count(&executor), 0);
}
#[test]
fn test_test_async_executor_recursive_spawning() {
use ops::DerefMut as _;
enum SpawningTask {
Init {
executor: sync_types::GenericArc<TestAsyncExecutor>,
n: u32,
},
WaitingForSpawn {
waiter: TestAsyncExecutorTaskWaiter<u32>,
},
}
impl Unpin for SpawningTask {}
impl future::Future for SpawningTask {
type Output = u32;
fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
match self.deref_mut() {
Self::Init { executor, n } => {
if *n == 0 {
task::Poll::Ready(0)
} else {
let mut waiter = TestAsyncExecutor::spawn(
executor,
SpawningTask::Init {
executor: executor.clone(),
n: *n - 1,
},
);
match future::Future::poll(pin::Pin::new(&mut waiter), cx) {
task::Poll::Ready(_) => {
unreachable!();
}
task::Poll::Pending => {
*self.deref_mut() = Self::WaitingForSpawn { waiter };
task::Poll::Pending
}
}
}
}
Self::WaitingForSpawn { waiter } => {
match future::Future::poll(pin::Pin::new(waiter), cx) {
task::Poll::Ready(n) => task::Poll::Ready(n + 1),
task::Poll::Pending => {
unreachable!();
}
}
}
}
}
}
let executor = TestAsyncExecutor::new();
let waiter = TestAsyncExecutor::spawn(
&executor,
SpawningTask::Init {
executor: executor.clone(),
n: 42,
},
);
TestAsyncExecutor::run_to_completion(&executor);
assert_eq!(waiter.take().unwrap(), 42);
assert_eq!(sync_types::GenericArc::strong_count(&executor), 1);
assert_eq!(sync_types::GenericArc::weak_count(&executor), 0);
}
#[test]
fn test_test_async_executor_wake_self() {
use ops::Deref as _;
enum SelfWakingTask {
Unpolled,
PolledOnce,
}
impl Unpin for SelfWakingTask {}
impl future::Future for SelfWakingTask {
type Output = u32;
fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
match self.deref() {
Self::Unpolled => {
cx.waker().wake_by_ref();
*self = Self::PolledOnce;
task::Poll::Pending
}
Self::PolledOnce => task::Poll::Ready(42),
}
}
}
let executor = TestAsyncExecutor::new();
let waiter = TestAsyncExecutor::spawn(&executor, SelfWakingTask::Unpolled);
let waiter = TestAsyncExecutor::spawn(&executor, waiter);
TestAsyncExecutor::run_to_completion(&executor);
assert_eq!(waiter.take().unwrap(), 42);
assert_eq!(sync_types::GenericArc::strong_count(&executor), 1);
assert_eq!(sync_types::GenericArc::weak_count(&executor), 0);
}