use crate::krse::cell::CausalCell;
use crate::krse::alloc::Track;
use crate::fiber::inner::raw::{self, Vtable};
use crate::fiber::inner::state::State;
use crate::fiber::inner::waker::waker_ref;
use crate::fiber::inner::{RawFiber, JoinHandle, JoinError};
use std::cell::UnsafeCell;
use std::future::Future;
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::ptr::{self, NonNull};
use std::task::{Context, Poll, Waker};
use std::marker::PhantomData;
use std::{fmt, mem};
pub(crate) struct Fiber<S: 'static> {
raw: RawFiber,
_p: PhantomData<S>,
}
unsafe impl<S: ScheduleSend + 'static> Send for Fiber<S> {}
pub(crate) type Result<T> = std::result::Result<T, JoinError>;
pub(crate) trait Schedule: Sized + 'static {
fn bind(&self, task: &Fiber<Self>);
fn release(&self, task: Fiber<Self>);
fn release_local(&self, task: &Fiber<Self>);
fn schedule(&self, task: Fiber<Self>);
}
pub(crate) trait ScheduleSend: Schedule + Send + Sync {}
#[repr(C)]
pub(super) struct Cell<T: Future> {
pub(super) header: Header,
pub(super) core: Core<T>,
pub(super) trailer: Trailer,
}
pub(super) struct Core<T: Future> {
stage: Stage<T>,
}
#[repr(C)]
pub(crate) struct Header {
pub(super) state: State,
pub(super) executor: CausalCell<Option<NonNull<()>>>,
pub(crate) queue_next: UnsafeCell<*const Header>,
pub(crate) owned_next: UnsafeCell<Option<NonNull<Header>>>,
pub(crate) owned_prev: UnsafeCell<Option<NonNull<Header>>>,
pub(super) vtable: &'static Vtable,
pub(super) future_causality: CausalCell<()>,
}
pub(super) struct Trailer {
pub(super) waker: CausalCell<MaybeUninit<Option<Waker>>>,
}
enum Stage<T: Future> {
Running(Track<T>),
Finished(Track<super::Result<T::Output>>),
Consumed,
}
impl<S: 'static> Fiber<S> {
pub(crate) unsafe fn from_raw(ptr: NonNull<Header>) -> Fiber<S> {
Fiber {
raw: RawFiber::from_raw(ptr),
_p: PhantomData,
}
}
pub(crate) fn header(&self) -> &Header {
self.raw.header()
}
pub(crate) fn into_raw(self) -> NonNull<Header> {
let raw = self.raw.into_raw();
mem::forget(self);
raw
}
}
impl<S: Schedule> Fiber<S> {
pub(crate) fn run<F>(self, mut executor: F) -> Option<Self>
where
F: FnMut() -> Option<NonNull<S>>,
{
if unsafe {
self.raw
.poll(&mut || executor().map(|ptr| ptr.cast::<()>()))
} {
Some(self)
} else {
mem::forget(self);
None
}
}
pub(crate) fn shutdown(self) {
self.raw.cancel_from_queue();
mem::forget(self);
}
}
pub(crate) fn joinable<T, S>(task: T) -> (Fiber<S>, JoinHandle<T::Output>)
where
T: Future + Send + 'static,
S: ScheduleSend,
{
let raw = RawFiber::new_joinable::<_, S>(task);
let task = Fiber {
raw,
_p: PhantomData,
};
let join = JoinHandle::new(raw);
(task, join)
}
pub(crate) fn joinable_local<T, S>(task: T) -> (Fiber<S>, JoinHandle<T::Output>)
where
T: Future + 'static,
S: Schedule,
{
let raw = RawFiber::new_joinable_local::<_, S>(task);
let task = Fiber {
raw,
_p: PhantomData,
};
let join = JoinHandle::new(raw);
(task, join)
}
impl<T: Future> Cell<T> {
pub(super) fn new<S>(future: T, state: State) -> Box<Cell<T>>
where
S: Schedule,
{
Box::new(Cell {
header: Header {
state,
executor: CausalCell::new(None),
queue_next: UnsafeCell::new(ptr::null()),
owned_next: UnsafeCell::new(None),
owned_prev: UnsafeCell::new(None),
vtable: raw::vtable::<T, S>(),
future_causality: CausalCell::new(()),
},
core: Core {
stage: Stage::Running(Track::new(future)),
},
trailer: Trailer {
waker: CausalCell::new(MaybeUninit::new(None)),
},
})
}
}
impl<T: Future> Core<T> {
pub(super) fn transition_to_consumed(&mut self) {
self.stage = Stage::Consumed
}
pub(super) fn poll<S>(&mut self, header: &Header) -> Poll<T::Output>
where
S: Schedule,
{
let res = {
let future = match &mut self.stage {
Stage::Running(tracked) => tracked.get_mut(),
_ => unreachable!("unexpected stage"),
};
let future = unsafe { Pin::new_unchecked(future) };
let waker_ref = waker_ref::<T, S>(header);
let mut cx = Context::from_waker(&*waker_ref);
future.poll(&mut cx)
};
if res.is_ready() {
self.stage = Stage::Consumed;
}
res
}
pub(super) fn store_output(&mut self, output: super::Result<T::Output>) {
self.stage = Stage::Finished(Track::new(output));
}
pub(super) unsafe fn read_output(&mut self, dst: *mut Track<super::Result<T::Output>>) {
use std::mem;
dst.write(match mem::replace(&mut self.stage, Stage::Consumed) {
Stage::Finished(output) => output,
_ => unreachable!("unexpected state"),
});
}
}
impl Header {
pub(super) fn executor(&self) -> Option<NonNull<()>> {
unsafe { self.executor.with(|ptr| *ptr) }
}
}
impl<S: 'static> Drop for Fiber<S> {
fn drop(&mut self) {
self.raw.drop_task();
}
}
impl<S> fmt::Debug for Fiber<S> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Fiber").finish()
}
}