use mountpoint_s3_crt_sys::*;
use std::ffi::CString;
use std::fmt::Debug;
use std::marker::PhantomPinned;
use std::pin::Pin;
use crate::common::allocator::Allocator;
use crate::common::common_library_init;
use crate::common::error::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskStatus {
RunReady,
Canceled,
}
impl TaskStatus {
fn to_aws_task_status(self) -> aws_task_status {
match self {
Self::RunReady => aws_task_status::AWS_TASK_STATUS_RUN_READY,
Self::Canceled => aws_task_status::AWS_TASK_STATUS_CANCELED,
}
}
fn from_aws_task_status(status: aws_task_status) -> Option<Self> {
if status == aws_task_status::AWS_TASK_STATUS_RUN_READY {
Some(Self::RunReady)
} else if status == aws_task_status::AWS_TASK_STATUS_CANCELED {
Some(Self::Canceled)
} else {
None
}
}
}
type TaskCallback = Box<dyn FnOnce(TaskStatus) + Send>;
struct TaskInner {
inner: aws_task,
callback: TaskCallback,
type_tag: CString,
_pinned: PhantomPinned,
}
impl Debug for TaskInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TaskInner")
.field("inner", &(&self.inner as *const _))
.field("type_tag", &self.type_tag)
.field("_pinned", &self._pinned)
.finish()
}
}
static_assertions::assert_impl_all!(TaskCallback: Send);
static_assertions::assert_impl_all!(CString: Send);
unsafe impl Send for TaskInner {}
#[derive(Debug)]
pub struct Task(Pin<Box<TaskInner>>);
impl Task {
pub fn init(allocator: &Allocator, callback: impl FnOnce(TaskStatus) + Send + 'static, type_tag: &str) -> Self {
common_library_init(allocator);
let mut task: Box<TaskInner> = Box::new(TaskInner {
inner: Default::default(),
callback: Box::new(callback),
type_tag: CString::new(type_tag).expect("type_tag must be CString-compatible"),
_pinned: Default::default(),
});
let task_ptr = &mut *task as *mut TaskInner;
unsafe {
aws_task_init(
&mut task.inner,
Some(task_fn),
task_ptr as *mut libc::c_void,
task.type_tag.as_ptr(),
);
}
Task(Box::into_pin(task))
}
pub(crate) unsafe fn into_aws_task_ptr(self) -> *mut aws_task {
let task = Box::leak(Pin::into_inner_unchecked(self.0));
&mut task.inner
}
pub fn run(self, status: TaskStatus) {
unsafe {
aws_task_run(self.into_aws_task_ptr(), status.to_aws_task_status());
}
}
}
unsafe extern "C" fn task_fn(task: *mut aws_task, arg: *mut libc::c_void, status: aws_task_status) {
let task_inner = Box::from_raw(arg as *mut TaskInner);
assert!(std::ptr::eq(task, &task_inner.inner));
let status = TaskStatus::from_aws_task_status(status).expect("got invalid aws_task_status from CRT");
(task_inner.callback)(status);
}
pub trait TaskScheduler: Send + Sync + 'static + crate::private::Sealed {
fn schedule_task_now(&self, task: Task) -> Result<(), Error>;
}
#[derive(Debug, Clone, Copy)]
pub struct BlockingTaskScheduler;
impl crate::private::Sealed for BlockingTaskScheduler {}
impl TaskScheduler for BlockingTaskScheduler {
fn schedule_task_now(&self, task: Task) -> Result<(), Error> {
task.run(TaskStatus::RunReady);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::mpsc;
#[test]
fn test_create_task() {
let (tx, rx) = mpsc::channel();
let start_num: u32 = 4;
let my_task = Task::init(
&Allocator::default(),
move |_| {
tx.send(start_num + 1).unwrap();
},
"test",
);
my_task.run(TaskStatus::RunReady);
let end_num = rx.try_recv().unwrap();
assert_eq!(end_num, start_num + 1);
}
}