use std::ffi::c_void;
use tokio::runtime::Handle;
use tokio_cronet_sys as sys;
use crate::{Error, Result};
pub(crate) struct Executor {
raw: sys::Cronet_ExecutorPtr,
context: *mut ExecutorContext,
}
struct ExecutorContext {
runtime: Handle,
}
impl Executor {
pub(crate) fn new() -> Result<Self> {
let runtime = Handle::try_current().map_err(|_| Error::TokioRuntimeRequired)?;
let context = Box::into_raw(Box::new(ExecutorContext { runtime }));
let raw = unsafe { sys::Cronet_Executor_CreateWith(Some(execute)) };
if raw.is_null() {
unsafe { drop(Box::from_raw(context)) };
return Err(Error::AllocationFailed("Tokio callback executor"));
}
unsafe { sys::Cronet_Executor_SetClientContext(raw, context.cast::<c_void>()) };
Ok(Self { raw, context })
}
pub(crate) fn as_ptr(&self) -> sys::Cronet_ExecutorPtr {
self.raw
}
pub(crate) fn handle(&self) -> &Handle {
&unsafe { &*self.context }.runtime
}
}
unsafe impl Send for Executor {}
unsafe impl Sync for Executor {}
impl Drop for Executor {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { sys::Cronet_Executor_Destroy(self.raw) };
self.raw = std::ptr::null_mut();
}
if !self.context.is_null() {
unsafe { drop(Box::from_raw(self.context)) };
self.context = std::ptr::null_mut();
}
}
}
struct Runnable {
raw: sys::Cronet_RunnablePtr,
}
unsafe impl Send for Runnable {}
impl Runnable {
fn run(mut self) {
unsafe {
sys::Cronet_Runnable_Run(self.raw);
sys::Cronet_Runnable_Destroy(self.raw);
}
self.raw = std::ptr::null_mut();
}
}
impl Drop for Runnable {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { sys::Cronet_Runnable_Destroy(self.raw) };
}
}
}
unsafe extern "C" fn execute(executor: sys::Cronet_ExecutorPtr, runnable: sys::Cronet_RunnablePtr) {
if runnable.is_null() {
return;
}
let context =
unsafe { sys::Cronet_Executor_GetClientContext(executor) }.cast::<ExecutorContext>();
if context.is_null() {
unsafe { sys::Cronet_Runnable_Destroy(runnable) };
return;
}
let runnable = Runnable { raw: runnable };
unsafe { &*context }
.runtime
.spawn(async move { runnable.run() });
}