use std::sync::{Arc, Weak};
use tokio::sync::Semaphore;
use crate::co::{AbortHandle, Handle, co_util, runtime};
#[inline(always)]
pub fn pool(capacity: isize) -> Pool {
match capacity {
1.. => Pool::new(capacity as usize),
c => {
let n = num_cpus::get();
let n = n.saturating_sub(-c as usize).max(1);
Pool::new(n)
}
}
}
#[derive(Clone)]
pub struct Pool(Arc<PoolInner>);
struct PoolInner(Semaphore);
impl Pool {
fn new(capacity: usize) -> Self {
Self(Arc::new(PoolInner(Semaphore::new(capacity))))
}
pub fn add_permits(&self, n: usize) {
self.0.0.add_permits(n);
}
pub fn forget_permits(&self, n: usize) -> usize {
self.0.0.forget_permits(n)
}
pub fn spawn<F>(&self, future: F) -> Handle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let sem = Arc::clone(&self.0);
crate::co::spawn(async move {
let _permit = sem.0.acquire().await.ok();
let result = future.await;
drop(_permit);
result
})
}
#[cfg(feature = "coroutine-heavy")]
pub fn spawn_blocking<F, R>(&self, f: F) -> Handle<crate::Result<R>>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let sem = Arc::clone(&self.0);
crate::co::spawn(async move {
let _permit = sem.0.acquire().await.ok();
let result = crate::co::spawn_blocking(f).co_join().await;
drop(_permit);
result
})
}
}
pub fn set<T: Send + 'static, I: IntoIterator<Item = Handle<T>>>(iter: I) -> Set<T> {
let mut set = Set::new();
set.extend(iter);
set
}
pub fn set_flatten<T: Send + 'static, E, I: IntoIterator<Item = Result<Handle<T>, E>>>(
iter: I,
) -> Result<Set<T>, (Set<T>, Vec<E>)> {
let mut set = Set::new();
let errors = set.extend_flatten(iter);
if errors.is_empty() {
Ok(set)
} else {
Err((set, errors))
}
}
pub struct Set<T> {
join_set: tokio::task::JoinSet<crate::Result<T>>,
abort_handles: Vec<Weak<AbortHandle>>,
}
impl<T> Drop for Set<T> {
fn drop(&mut self) {
for x in &self.abort_handles {
if let Some(x) = x.upgrade() {
x.abort()
}
}
}
}
impl<T: Send + 'static> Set<T> {
fn new() -> Self {
Self {
join_set: tokio::task::JoinSet::new(),
abort_handles: Vec::new(),
}
}
pub fn add(&mut self, handle: Handle<T>) {
self.gc_handles(Some(1));
self.add_internal(handle);
}
pub fn extend<I: IntoIterator<Item = Handle<T>>>(&mut self, iter: I) {
let iter = iter.into_iter();
self.gc_handles(iter.size_hint().1);
for handle in iter {
self.add_internal(handle);
}
}
pub fn extend_flatten<E, I: IntoIterator<Item = Result<Handle<T>, E>>>(
&mut self,
iter: I,
) -> Vec<E> {
let iter = iter.into_iter();
let mut errors = vec![];
self.gc_handles(iter.size_hint().1);
for handle in iter {
match handle {
Ok(handle) => self.add_internal(handle),
Err(e) => errors.push(e),
}
}
errors
}
fn add_internal(&mut self, handle: Handle<T>) {
let abort_handle = Arc::new(handle.abort_handle());
self.abort_handles.push(Arc::downgrade(&abort_handle));
self.join_set.spawn_on(
async move {
let result = handle.co_join().await;
drop(abort_handle);
result
},
runtime::background().handle(),
);
}
fn gc_handles(&mut self, add: Option<usize>) {
if let Some(add) = add
&& self.abort_handles.capacity() - self.abort_handles.len() >= add
{
return;
}
self.abort_handles.retain(|x| x.strong_count() != 0);
}
pub async fn next(&mut self) -> Option<crate::Result<T>> {
let result = self.join_set.join_next().await?;
match result {
Err(join_error) => match co_util::handle_join_error(join_error) {
Err(e) => Some(Err(e)),
Ok(_) => Some(Err(crate::fmterr!("aborted"))),
},
Ok(x) => Some(x),
}
}
#[inline]
pub fn block(&mut self) -> Option<crate::Result<T>> {
runtime::foreground().block_on(async move { self.next().await })
}
}