use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use core::task::{Context, Poll};
use super::runtime::{JoinError, JoinManubrium, RuntimeGenerare};
#[derive(Debug, Default)]
pub struct TesseraAbrogationis {
flag: AtomicBool,
waker: std::sync::Mutex<Option<core::task::Waker>>,
}
impl TesseraAbrogationis {
pub fn abrogare(&self) {
self.flag.store(true, Ordering::Release);
let waker = self
.waker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.take();
if let Some(w) = waker {
w.wake();
}
}
#[inline]
pub fn est_abrogata(&self) -> bool {
self.flag.load(Ordering::Acquire)
}
pub(crate) fn registrare(&self, cx: &Context<'_>) {
*self
.waker
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(cx.waker().clone());
}
}
static FIBRA_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FibraId(u64);
impl FibraId {
#[inline]
pub(crate) fn new() -> Self {
FibraId(FIBRA_COUNTER.fetch_add(1, Ordering::Relaxed))
}
#[inline]
pub fn value(&self) -> u64 {
self.0
}
}
impl core::fmt::Display for FibraId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Fibra({})", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FibraStatus {
Pendens,
Currens,
Perfectus,
Defectus,
Abrogatus,
}
#[derive(Debug, Clone)]
pub enum FibraError {
Abrogatus,
Panic(String),
TemporisExcessus,
InfansDefectus(Box<FibraError>),
Alius(String),
}
impl core::fmt::Display for FibraError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
FibraError::Abrogatus => write!(f, "fiber was cancelled"),
FibraError::Panic(msg) => write!(f, "fiber panicked: {msg}"),
FibraError::TemporisExcessus => write!(f, "fiber timed out"),
FibraError::InfansDefectus(e) => write!(f, "child fiber failed: {e}"),
FibraError::Alius(msg) => write!(f, "fiber error: {msg}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FibraError {}
pub type FibraExitus<A> = Result<A, FibraError>;
pub struct Fibra<A> {
id: FibraId,
inner: Pin<Box<dyn Future<Output = FibraExitus<A>> + Send>>,
cancelled: Arc<TesseraAbrogationis>,
}
impl<A> Fibra<A> {
pub fn new<F>(future: F) -> Self
where
F: Future<Output = A> + Send + 'static,
A: Send + 'static,
{
let cancelled = Arc::new(TesseraAbrogationis::default());
let cancelled_clone = cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
if cancelled_clone.est_abrogata() {
return Err(FibraError::Abrogatus);
}
Ok(future.await)
}),
cancelled,
}
}
pub fn purus(value: A) -> Self
where
A: Send + 'static,
{
Fibra {
id: FibraId::new(),
inner: Box::pin(async move { Ok(value) }),
cancelled: Arc::new(TesseraAbrogationis::default()),
}
}
pub fn deficere(error: FibraError) -> Self
where
A: Send + 'static,
{
Fibra {
id: FibraId::new(),
inner: Box::pin(async move { Err(error) }),
cancelled: Arc::new(TesseraAbrogationis::default()),
}
}
#[inline]
pub fn id(&self) -> FibraId {
self.id
}
#[inline]
pub fn is_cancelled(&self) -> bool {
self.cancelled.est_abrogata()
}
#[inline]
pub fn cancellation_token(&self) -> Arc<TesseraAbrogationis> {
self.cancelled.clone()
}
#[inline]
pub fn mutare<B, F>(self, f: F) -> Fibra<B>
where
F: FnOnce(A) -> B + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let result = self.await;
result.map(f)
}),
cancelled,
}
}
#[inline]
pub fn map<B, F>(self, f: F) -> Fibra<B>
where
F: FnOnce(A) -> B + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
self.mutare(f)
}
#[inline]
pub fn ligare<B, F>(self, f: F) -> Fibra<B>
where
F: FnOnce(A) -> Fibra<B> + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let result = self.await;
match result {
Ok(a) => f(a).await,
Err(e) => Err(e),
}
}),
cancelled,
}
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> Fibra<B>
where
F: FnOnce(A) -> Fibra<B> + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
self.ligare(f)
}
pub fn applicare<B, F>(self, ff: Fibra<F>) -> Fibra<B>
where
F: FnOnce(A) -> B + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let f_result = ff.await;
let a_result = self.await;
match (f_result, a_result) {
(Ok(f), Ok(a)) => Ok(f(a)),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e),
}
}),
cancelled,
}
}
pub fn recuperare<F>(self, handler: F) -> Fibra<A>
where
F: FnOnce(FibraError) -> A + Send + 'static,
A: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let result = self.await;
Ok(result.unwrap_or_else(handler))
}),
cancelled,
}
}
pub fn recuperare_cum<F>(self, handler: F) -> Fibra<A>
where
F: FnOnce(FibraError) -> Fibra<A> + Send + 'static,
A: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let result = self.await;
match result {
Ok(a) => Ok(a),
Err(e) => handler(e).await,
}
}),
cancelled,
}
}
pub fn assecurare<F, Fut>(self, finalizer: F) -> Fibra<A>
where
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send + 'static,
A: Send + 'static,
{
let cancelled = self.cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let result = self.await;
finalizer().await;
result
}),
cancelled,
}
}
}
impl<A> Future for Fibra<A> {
type Output = FibraExitus<A>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.cancelled.registrare(cx);
if self.cancelled.est_abrogata() {
return Poll::Ready(Err(FibraError::Abrogatus));
}
self.inner.as_mut().poll(cx)
}
}
pub struct FibraManubrium<A> {
id: FibraId,
join_handle: JoinManubrium<FibraExitus<A>>,
cancelled: Arc<TesseraAbrogationis>,
}
impl<A> FibraManubrium<A> {
#[inline]
pub fn new(
id: FibraId,
join_handle: JoinManubrium<FibraExitus<A>>,
cancelled: Arc<TesseraAbrogationis>,
) -> Self {
FibraManubrium {
id,
join_handle,
cancelled,
}
}
#[inline]
pub fn id(&self) -> FibraId {
self.id
}
#[inline]
pub fn abrogare(&self) {
self.cancelled.abrogare();
}
#[inline]
pub fn cancel(&self) {
self.abrogare();
}
#[inline]
pub fn is_cancelled(&self) -> bool {
self.cancelled.est_abrogata()
}
pub async fn abrogare_et_conjungere(self) -> FibraExitus<A> {
self.abrogare();
self.conjungere().await
}
#[inline]
pub async fn conjungere(self) -> FibraExitus<A> {
match self.join_handle.await {
Ok(result) => result,
Err(JoinError::Cancelled) => Err(FibraError::Abrogatus),
Err(JoinError::Panic(msg)) => Err(FibraError::Panic(msg)),
Err(JoinError::Other(msg)) => Err(FibraError::Alius(msg)),
}
}
#[inline]
pub async fn join(self) -> FibraExitus<A> {
self.conjungere().await
}
pub fn poll_conjunctio(&mut self, cx: &mut Context<'_>) -> Poll<FibraExitus<A>> {
match Pin::new(&mut self.join_handle).poll(cx) {
Poll::Ready(Ok(result)) => Poll::Ready(result),
Poll::Ready(Err(JoinError::Cancelled)) => Poll::Ready(Err(FibraError::Abrogatus)),
Poll::Ready(Err(JoinError::Panic(msg))) => Poll::Ready(Err(FibraError::Panic(msg))),
Poll::Ready(Err(JoinError::Other(msg))) => Poll::Ready(Err(FibraError::Alius(msg))),
Poll::Pending => Poll::Pending,
}
}
}
pub fn par<A, B>(fa: Fibra<A>, fb: Fibra<B>) -> Fibra<(A, B)>
where
A: Send + 'static,
B: Send + 'static,
{
let cancelled = Arc::new(TesseraAbrogationis::default());
let token_a = fa.cancellation_token();
let token_b = fb.cancellation_token();
let mut fa = Some(fa);
let mut fb = Some(fb);
let mut ra: Option<A> = None;
let mut rb: Option<B> = None;
Fibra {
id: FibraId::new(),
inner: Box::pin(core::future::poll_fn(move |cx| {
if ra.is_none()
&& let Some(f) = fa.as_mut()
{
match Pin::new(f).poll(cx) {
Poll::Ready(Ok(a)) => {
ra = Some(a);
fa = None;
}
Poll::Ready(Err(e)) => {
token_b.abrogare(); return Poll::Ready(Err(e));
}
Poll::Pending => {}
}
}
if rb.is_none()
&& let Some(f) = fb.as_mut()
{
match Pin::new(f).poll(cx) {
Poll::Ready(Ok(b)) => {
rb = Some(b);
fb = None;
}
Poll::Ready(Err(e)) => {
token_a.abrogare();
return Poll::Ready(Err(e));
}
Poll::Pending => {}
}
}
match (ra.take(), rb.take()) {
(Some(a), Some(b)) => Poll::Ready(Ok((a, b))),
(a, b) => {
ra = a;
rb = b;
Poll::Pending
}
}
})),
cancelled,
}
}
#[inline]
pub fn zip_par<A, B>(fa: Fibra<A>, fb: Fibra<B>) -> Fibra<(A, B)>
where
A: Send + 'static,
B: Send + 'static,
{
par(fa, fb)
}
pub fn certamen<A>(fa: Fibra<A>, fb: Fibra<A>) -> Fibra<A>
where
A: Send + 'static,
{
let cancelled = Arc::new(TesseraAbrogationis::default());
let token_a = fa.cancellation_token();
let token_b = fb.cancellation_token();
let mut fa = Some(fa);
let mut fb = Some(fb);
Fibra {
id: FibraId::new(),
inner: Box::pin(core::future::poll_fn(move |cx| {
if let Some(f) = fa.as_mut()
&& let Poll::Ready(r) = Pin::new(f).poll(cx)
{
fa = None;
token_b.abrogare();
return Poll::Ready(r);
}
if let Some(f) = fb.as_mut()
&& let Poll::Ready(r) = Pin::new(f).poll(cx)
{
fb = None;
token_a.abrogare();
return Poll::Ready(r);
}
Poll::Pending
})),
cancelled,
}
}
#[inline]
pub fn race<A>(fa: Fibra<A>, fb: Fibra<A>) -> Fibra<A>
where
A: Send + 'static,
{
certamen(fa, fb)
}
pub fn certamen_multi<A>(fibers: Vec<Fibra<A>>) -> Fibra<A>
where
A: Send + 'static,
{
if fibers.is_empty() {
return Fibra::deficere(FibraError::Alius("no fibers to race".into()));
}
let cancelled = Arc::new(TesseraAbrogationis::default());
let cancelled_clone = cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
for fiber in fibers {
let result = fiber.await;
if result.is_ok() {
return result;
}
}
Err(FibraError::Alius("all fibers failed".into()))
}),
cancelled: cancelled_clone,
}
}
pub fn sequentia<A>(fibers: Vec<Fibra<A>>) -> Fibra<Vec<A>>
where
A: Send + 'static,
{
let cancelled = Arc::new(TesseraAbrogationis::default());
let cancelled_clone = cancelled.clone();
Fibra {
id: FibraId::new(),
inner: Box::pin(async move {
let mut results = Vec::with_capacity(fibers.len());
for fiber in fibers {
if cancelled_clone.est_abrogata() {
return Err(FibraError::Abrogatus);
}
let result = fiber.await?;
results.push(result);
}
Ok(results)
}),
cancelled,
}
}
#[inline]
pub fn sequence<A>(fibers: Vec<Fibra<A>>) -> Fibra<Vec<A>>
where
A: Send + 'static,
{
sequentia(fibers)
}
pub fn par_omnes<A>(fibers: Vec<Fibra<A>>) -> Fibra<Vec<A>>
where
A: Send + 'static,
{
let cancelled = Arc::new(TesseraAbrogationis::default());
let tokens: Vec<Arc<TesseraAbrogationis>> =
fibers.iter().map(Fibra::cancellation_token).collect();
let mut slots: Vec<Option<Fibra<A>>> = fibers.into_iter().map(Some).collect();
let mut results: Vec<Option<A>> = core::iter::repeat_with(|| None).take(slots.len()).collect();
Fibra {
id: FibraId::new(),
inner: Box::pin(core::future::poll_fn(move |cx| {
for (i, slot) in slots.iter_mut().enumerate() {
if results[i].is_some() {
continue;
}
if let Some(f) = slot.as_mut() {
match Pin::new(f).poll(cx) {
Poll::Ready(Ok(a)) => {
results[i] = Some(a);
*slot = None;
}
Poll::Ready(Err(e)) => {
for token in &tokens {
token.abrogare();
}
return Poll::Ready(Err(e));
}
Poll::Pending => {}
}
}
}
if results.iter().all(Option::is_some) {
let done = results.iter_mut().map(|r| r.take().unwrap()).collect();
Poll::Ready(Ok(done))
} else {
Poll::Pending
}
})),
cancelled,
}
}
#[inline]
pub fn par_sequence<A>(fibers: Vec<Fibra<A>>) -> Fibra<Vec<A>>
where
A: Send + 'static,
{
par_omnes(fibers)
}
pub fn transire<A, B, F>(items: Vec<A>, f: F) -> Fibra<Vec<B>>
where
A: Send + 'static,
B: Send + 'static,
F: Fn(A) -> Fibra<B> + Send + 'static,
{
let fibers: Vec<Fibra<B>> = items.into_iter().map(f).collect();
sequentia(fibers)
}
pub fn transire_par<A, B, F>(items: Vec<A>, f: F) -> Fibra<Vec<B>>
where
A: Send + 'static,
B: Send + 'static,
F: Fn(A) -> Fibra<B> + Send + 'static,
{
let fibers: Vec<Fibra<B>> = items.into_iter().map(f).collect();
par_omnes(fibers)
}
pub struct FibraAmbitus<R: RuntimeGenerare> {
children: Vec<Arc<TesseraAbrogationis>>,
_runtime: PhantomData<R>,
}
impl<R: RuntimeGenerare> FibraAmbitus<R> {
#[inline]
pub fn new() -> Self {
FibraAmbitus {
children: Vec::with_capacity(8),
_runtime: PhantomData,
}
}
#[inline]
pub fn spawn<A, F>(&mut self, future: F) -> FibraManubrium<A>
where
F: Future<Output = A> + Send + 'static,
A: Send + 'static,
{
let fibra = Fibra::new(future);
let id = fibra.id();
let cancelled = fibra.cancellation_token();
self.children.push(cancelled.clone());
let join_handle = R::spawn(fibra);
FibraManubrium::new(id, join_handle, cancelled)
}
#[inline]
pub fn cancel_all(&self) {
for child in &self.children {
child.abrogare();
}
}
}
impl<R: RuntimeGenerare> Default for FibraAmbitus<R> {
fn default() -> Self {
Self::new()
}
}
impl<R: RuntimeGenerare> Drop for FibraAmbitus<R> {
fn drop(&mut self) {
self.cancel_all();
}
}
#[inline]
pub fn fibra<A, F>(future: F) -> Fibra<A>
where
F: Future<Output = A> + Send + 'static,
A: Send + 'static,
{
Fibra::new(future)
}
#[inline]
pub fn purus<A>(value: A) -> Fibra<A>
where
A: Send + 'static,
{
Fibra::purus(value)
}
#[inline]
pub fn deficere<A>(error: FibraError) -> Fibra<A>
where
A: Send + 'static,
{
Fibra::deficere(error)
}
#[inline]
pub fn spawn<R, A, F>(future: F) -> FibraManubrium<A>
where
R: RuntimeGenerare,
F: Future<Output = A> + Send + 'static,
A: Send + 'static,
{
let fibra = Fibra::new(future);
let id = fibra.id();
let cancelled = fibra.cancellation_token();
let join_handle = R::spawn(fibra);
FibraManubrium::new(id, join_handle, cancelled)
}
#[inline]
pub fn furca<R, A, F>(future: F) -> Fibra<FibraManubrium<A>>
where
R: RuntimeGenerare,
F: Future<Output = A> + Send + 'static,
A: Send + 'static,
{
Fibra::purus(spawn::<R, A, F>(future))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fibra_id_unique() {
let id1 = FibraId::new();
let id2 = FibraId::new();
assert_ne!(id1, id2);
}
#[test]
fn test_fibra_id_display() {
let id = FibraId::new();
let display = alloc::format!("{id}");
assert!(display.starts_with("Fibra("));
}
#[test]
fn test_fibra_error_display() {
let err = FibraError::Abrogatus;
assert!(alloc::format!("{err}").contains("cancelled"));
let err = FibraError::Panic("oops".into());
assert!(alloc::format!("{err}").contains("panic"));
let err = FibraError::TemporisExcessus;
assert!(alloc::format!("{err}").contains("timed out"));
}
#[test]
fn test_fibra_purus() {
let fiber = Fibra::purus(42);
assert!(!fiber.is_cancelled());
}
#[test]
fn test_fibra_deficere() {
let fiber: Fibra<i32> = Fibra::deficere(FibraError::Abrogatus);
assert!(!fiber.is_cancelled());
}
#[test]
fn test_fibra_cancellation_token() {
let fiber = Fibra::purus(42);
let token = fiber.cancellation_token();
assert!(!token.est_abrogata());
token.abrogare();
assert!(fiber.is_cancelled());
}
#[test]
fn test_fibra_status() {
assert_eq!(FibraStatus::Pendens, FibraStatus::Pendens);
assert_ne!(FibraStatus::Pendens, FibraStatus::Currens);
}
#[test]
#[should_panic(expected = "no async runtime is enabled")]
fn test_ambitus_cancel_all_cancels_spawned_fibers() {
use crate::async_core::runtime::NullRuntime;
let mut scope: FibraAmbitus<NullRuntime> = FibraAmbitus::new();
let _handle = scope.spawn(async { 42 });
scope.cancel_all();
}
#[test]
#[should_panic(expected = "no async runtime is enabled")]
fn test_ambitus_drop_cancels_spawned_fibers() {
use crate::async_core::runtime::NullRuntime;
let mut scope: FibraAmbitus<NullRuntime> = FibraAmbitus::new();
let _handle = scope.spawn(async { 42 });
drop(scope);
}
}