use crate::get_task_from_context;
use crate::runtime::local_executor;
use crate::runtime::task::Task;
use crate::sync::wait_groups::AsyncWaitGroup;
use std::cell::UnsafeCell;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
pub struct WaitLocalWaitGroup<'wait_group> {
wait_group: &'wait_group LocalWaitGroup,
no_send_marker: std::marker::PhantomData<*const ()>,
}
impl<'wait_group> WaitLocalWaitGroup<'wait_group> {
#[inline(always)]
pub fn new(wait_group: &'wait_group LocalWaitGroup) -> Self {
Self {
wait_group,
no_send_marker: std::marker::PhantomData,
}
}
}
impl Future for WaitLocalWaitGroup<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let inner = this.wait_group.get_inner();
if inner.count != 0 {
let task = unsafe { get_task_from_context!(cx) };
inner.waited_tasks.push(task);
return Poll::Pending;
}
Poll::Ready(())
}
}
struct Inner {
count: usize,
waited_tasks: Vec<Task>,
}
pub struct LocalWaitGroup {
inner: UnsafeCell<Inner>,
no_send_marker: std::marker::PhantomData<*const ()>,
}
impl LocalWaitGroup {
pub const fn new() -> Self {
Self {
inner: UnsafeCell::new(Inner {
count: 0,
waited_tasks: Vec::new(),
}),
no_send_marker: std::marker::PhantomData,
}
}
#[inline(always)]
#[allow(clippy::mut_from_ref, reason = "this is local and Sync")]
fn get_inner(&self) -> &mut Inner {
unsafe { &mut *self.inner.get() }
}
}
impl AsyncWaitGroup for LocalWaitGroup {
#[inline(always)]
fn add(&self, count: usize) {
self.get_inner().count += count;
}
#[inline(always)]
fn count(&self) -> usize {
self.get_inner().count
}
#[inline(always)]
fn done(&self) -> usize {
let inner = self.get_inner();
inner.count -= 1;
if inner.count == 0 {
let executor = local_executor();
for task in inner.waited_tasks.drain(..) {
executor.exec_task(task);
}
}
inner.count
}
#[inline(always)]
#[allow(clippy::future_not_send, reason = "It is `local`")]
fn wait(&self) -> impl Future<Output = ()> {
WaitLocalWaitGroup::new(self)
}
}
impl Default for LocalWaitGroup {
fn default() -> Self {
Self::new()
}
}
unsafe impl Sync for LocalWaitGroup {}
#[allow(dead_code, reason = "It is used only in compile tests")]
fn test_compile_local_wait_group() {}
#[cfg(test)]
mod tests {
use super::*;
use crate as orengine;
use crate::local::Local;
use crate::runtime::local_executor;
use crate::yield_now;
use std::rc::Rc;
#[orengine::test::test_local]
fn test_local_wg_many_wait_one() {
let check_value = Local::new(false);
let wait_group = Rc::new(LocalWaitGroup::new());
wait_group.inc();
for _ in 0..5 {
let check_value = check_value.clone();
let wait_group = wait_group.clone();
local_executor().spawn_local(async move {
wait_group.wait().await;
assert!(*check_value.borrow(), "not waited");
});
}
yield_now().await;
*check_value.borrow_mut() = true;
wait_group.done();
}
#[orengine::test::test_local]
fn test_local_wg_one_wait_many() {
let check_value = Local::new(5);
let wait_group = Rc::new(LocalWaitGroup::new());
wait_group.add(5);
for _ in 0..5 {
let check_value = check_value.clone();
let wait_group = wait_group.clone();
local_executor().spawn_local(async move {
yield_now().await;
*check_value.borrow_mut() -= 1;
wait_group.done();
});
}
wait_group.wait().await;
assert_eq!(*check_value.borrow(), 0, "not waited");
}
}