use core::future::Future;
use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Context, Poll, Waker};
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Weak};
use std::task::Wake;
use crate::rt::inbox::{Inbox, Link, Linked, Pop};
use crate::sync::atomic_waker::AtomicWaker;
use crate::task::{AbortHandle, Id, Join, JoinError, Task};
struct Shared {
ready: Inbox<Queued>,
awaiter: AtomicWaker,
}
struct SlotWake {
link: Link,
shared: Weak<Shared>,
slot: usize,
queued: AtomicBool,
}
impl Wake for SlotWake {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
if !self.queued.swap(true, Ordering::AcqRel)
&& let Some(shared) = self.shared.upgrade()
{
shared.ready.push(Queued(Arc::clone(self)));
shared.awaiter.wake();
}
}
}
struct Queued(Arc<SlotWake>);
unsafe impl Linked for Queued {
fn into_link(self) -> NonNull<Link> {
let wake = Arc::into_raw(self.0);
unsafe { NonNull::new_unchecked(core::ptr::addr_of!((*wake).link).cast_mut()) }
}
unsafe fn from_link(link: NonNull<Link>) -> Self {
let offset = core::mem::offset_of!(SlotWake, link);
Self(unsafe { Arc::from_raw(link.as_ptr().cast::<u8>().sub(offset).cast::<SlotWake>()) })
}
}
struct Slot<T> {
wake: Arc<SlotWake>,
waker: Waker,
member: Option<Member<T>>,
}
struct Member<T> {
join: Join<T>,
abort: AbortHandle,
}
pub struct JoinSet<T> {
slots: Vec<Slot<T>>,
free: Vec<usize>,
len: usize,
shared: Arc<Shared>,
}
impl<T> Default for JoinSet<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> JoinSet<T> {
pub fn new() -> Self {
Self {
slots: Vec::new(),
free: Vec::new(),
len: 0,
shared: Arc::new(Shared {
ready: Inbox::new(),
awaiter: AtomicWaker::new(),
}),
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn insert(&mut self, task: Task<T>) -> AbortHandle {
let abort = task.abort_handle();
let member = Member {
join: task.join(),
abort: abort.clone(),
};
let slot = match self.free.pop() {
Some(slot) => slot,
None => {
let slot = self.slots.len();
let wake = Arc::new(SlotWake {
link: Link::new(),
shared: Arc::downgrade(&self.shared),
slot,
queued: AtomicBool::new(false),
});
self.slots.push(Slot {
waker: Waker::from(Arc::clone(&wake)),
wake,
member: None,
});
slot
}
};
let place = &mut self.slots[slot];
place.member = Some(member);
self.len += 1;
if !place.wake.queued.swap(true, Ordering::AcqRel) {
self.shared.ready.push(Queued(Arc::clone(&place.wake)));
}
abort
}
pub fn spawn<F>(&mut self, future: F) -> AbortHandle
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.insert(crate::task::spawn(future))
}
pub fn spawn_local<F>(&mut self, future: F) -> AbortHandle
where
F: Future<Output = T> + 'static,
T: 'static,
{
self.insert(crate::task::spawn_local(future))
}
pub fn spawn_blocking<F>(&mut self, f: F) -> AbortHandle
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
self.insert(crate::task::spawn_blocking(f))
}
pub fn spawn_on<F>(&mut self, future: F, handle: &crate::runtime::RuntimeHandle) -> AbortHandle
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.insert(handle.spawn(future))
}
pub async fn join_next(&mut self) -> Option<Result<T, JoinError>> {
core::future::poll_fn(|cx| self.poll_join_next(cx))
.await
.map(|out| out.map(|(_, value)| value))
}
pub async fn join_next_with_id(&mut self) -> Option<Result<(Id, T), JoinError>> {
core::future::poll_fn(|cx| self.poll_join_next(cx)).await
}
pub fn try_join_next(&mut self) -> Option<Result<T, JoinError>> {
let mut cx = Context::from_waker(Waker::noop());
match self.poll_join_next(&mut cx) {
Poll::Ready(Some(out)) => Some(out.map(|(_, value)| value)),
_ => None,
}
}
pub fn poll_join_next(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Option<Result<(Id, T), JoinError>>> {
if self.len == 0 {
return Poll::Ready(None);
}
self.shared.awaiter.register(cx.waker());
for _ in 0..self.len {
let wake = match unsafe { self.shared.ready.pop() } {
Pop::Item(Queued(wake)) => wake,
Pop::Empty | Pop::Busy => return Poll::Pending,
};
wake.queued.store(false, Ordering::Release);
let slot = wake.slot;
let Some(place) = self
.slots
.get_mut(slot)
.filter(|p| Arc::ptr_eq(&p.wake, &wake))
else {
continue;
};
let Some(member) = place.member.as_mut() else {
continue;
};
let mut member_cx = Context::from_waker(&place.waker);
if let Poll::Ready(out) = Pin::new(&mut member.join).poll(&mut member_cx) {
let id = member.abort.id();
place.member = None;
self.free.push(slot);
self.len -= 1;
return Poll::Ready(Some(out.map(|value| (id, value))));
}
}
cx.waker().wake_by_ref();
Poll::Pending
}
#[expect(clippy::panic, reason = "documented under `# Panics`")]
pub async fn join_all(mut self) -> Vec<T> {
let mut outputs = Vec::with_capacity(self.len);
while let Some(out) = self.join_next().await {
match out {
Ok(value) => outputs.push(value),
Err(error) => match error.try_into_panic() {
Ok(panic) => std::panic::resume_unwind(panic),
Err(error) => panic!("`JoinSet::join_all`: {error}"),
},
}
}
outputs
}
pub fn abort_all(&mut self) {
for member in self.slots.iter().filter_map(|p| p.member.as_ref()) {
member.abort.abort();
}
}
pub fn detach_all(&mut self) {
for place in self.slots.drain(..) {
if let Some(member) = place.member {
member.join.detach();
}
}
self.free.clear();
self.len = 0;
unsafe { self.shared.ready.drain(drop) };
}
pub async fn shutdown(&mut self) {
self.abort_all();
while self.join_next().await.is_some() {}
}
}
impl<T, F> FromIterator<F> for JoinSet<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
fn from_iter<I: IntoIterator<Item = F>>(iter: I) -> Self {
let mut set = Self::new();
for future in iter {
set.spawn(future);
}
set
}
}
impl<T, F> Extend<F> for JoinSet<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
fn extend<I: IntoIterator<Item = F>>(&mut self, iter: I) {
for future in iter {
self.spawn(future);
}
}
}
impl<T> fmt::Debug for JoinSet<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JoinSet").field("len", &self.len).finish()
}
}