use metrics::{counter, gauge};
use std::future::Future;
use std::pin::Pin;
use std::ptr;
use std::sync::OnceLock;
use std::sync::atomic::AtomicUsize;
use std::sync::{
Arc,
atomic::{AtomicPtr, AtomicU8, Ordering},
};
use std::task::{Context, Poll, Waker};
use crate::executor::ExecutorHandle;
use crate::waker;
pub(crate) static NEXT_TASK_ID: AtomicUsize = AtomicUsize::new(0);
const TASK_STATE_IDLE: u8 = 0;
const TASK_STATE_POLLING: u8 = 1;
const TASK_STATE_COMPLETED: u8 = 2;
pub type TaskFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
pub struct Task {
pub id: usize,
pub state: AtomicU8,
pub future: AtomicPtr<TaskFuture>,
pub waker: OnceLock<Waker>,
}
impl Task {
pub fn new(future: TaskFuture) -> Self {
let id = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed);
let ptr = Box::into_raw(Box::new(future));
Self {
id,
state: AtomicU8::new(TASK_STATE_IDLE),
future: AtomicPtr::new(ptr),
waker: OnceLock::new(),
}
}
pub fn try_take(&self) -> Option<TaskFuture> {
if self
.state
.compare_exchange(
TASK_STATE_IDLE,
TASK_STATE_POLLING,
Ordering::AcqRel,
Ordering::Relaxed,
)
.is_err()
{
return None;
}
let ptr = self.future.swap(ptr::null_mut(), Ordering::Acquire);
if ptr.is_null() {
self.state.store(TASK_STATE_IDLE, Ordering::Release);
return None;
}
unsafe { Some(*Box::from_raw(ptr)) }
}
pub fn poll(&self, mut future: TaskFuture, waker: &Waker) {
let mut ctx = Context::from_waker(waker);
match future.as_mut().poll(&mut ctx) {
Poll::Ready(()) => {
self.state.store(TASK_STATE_COMPLETED, Ordering::Release);
counter!("yot_run_tasks_completed_total").increment(1);
gauge!("yot_run_tasks_pending_current").decrement(1.0);
}
Poll::Pending => {
self.release_after_poll(future);
}
}
}
pub fn get_or_init_waker(self: &Arc<Self>, executor_handle: Arc<ExecutorHandle>) -> &Waker {
self.waker
.get_or_init(|| waker::task_waker(self.clone(), executor_handle))
}
fn release_after_poll(&self, future: TaskFuture) {
let ptr = Box::into_raw(Box::new(future));
self.future.store(ptr, Ordering::Release);
self.state.store(TASK_STATE_IDLE, Ordering::Release);
}
}
impl Drop for Task {
fn drop(&mut self) {
let ptr = self.future.swap(ptr::null_mut(), Ordering::AcqRel);
if !ptr.is_null() {
unsafe {
drop(Box::from_raw(ptr));
}
}
}
}