use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::UserDataTrait;
use crate::lua_value::{LuaUserdata, LuaValue};
use crate::lua_vm::lua_ref::RefId;
use crate::lua_vm::{LuaResult, LuaVM};
pub enum AsyncReturnValue {
Value(LuaValue),
String(String),
UserData(LuaUserdata),
Table(Vec<(AsyncReturnValue, AsyncReturnValue)>),
}
pub trait IntoAsyncLua {
fn into_async_lua(self) -> Vec<AsyncReturnValue>;
}
impl AsyncReturnValue {
#[inline]
pub fn nil() -> Self {
AsyncReturnValue::Value(LuaValue::nil())
}
#[inline]
pub fn integer(n: i64) -> Self {
AsyncReturnValue::Value(LuaValue::integer(n))
}
#[inline]
pub fn float(n: f64) -> Self {
AsyncReturnValue::Value(LuaValue::float(n))
}
#[inline]
pub fn boolean(b: bool) -> Self {
AsyncReturnValue::Value(LuaValue::boolean(b))
}
#[inline]
pub fn string(s: impl Into<String>) -> Self {
AsyncReturnValue::String(s.into())
}
#[inline]
pub fn userdata<T: UserDataTrait>(data: T) -> Self {
AsyncReturnValue::UserData(LuaUserdata::new(data))
}
#[inline]
pub fn table(entries: Vec<(AsyncReturnValue, AsyncReturnValue)>) -> Self {
AsyncReturnValue::Table(entries)
}
}
impl From<i64> for AsyncReturnValue {
fn from(n: i64) -> Self {
AsyncReturnValue::integer(n)
}
}
impl From<f64> for AsyncReturnValue {
fn from(n: f64) -> Self {
AsyncReturnValue::float(n)
}
}
impl From<bool> for AsyncReturnValue {
fn from(b: bool) -> Self {
AsyncReturnValue::boolean(b)
}
}
impl From<String> for AsyncReturnValue {
fn from(s: String) -> Self {
AsyncReturnValue::String(s)
}
}
impl From<&str> for AsyncReturnValue {
fn from(s: &str) -> Self {
AsyncReturnValue::String(s.to_string())
}
}
impl From<LuaValue> for AsyncReturnValue {
fn from(v: LuaValue) -> Self {
AsyncReturnValue::Value(v)
}
}
impl From<LuaUserdata> for AsyncReturnValue {
fn from(ud: LuaUserdata) -> Self {
AsyncReturnValue::UserData(ud)
}
}
impl IntoAsyncLua for () {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
Vec::new()
}
}
impl IntoAsyncLua for AsyncReturnValue {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![self]
}
}
impl IntoAsyncLua for LuaValue {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::from(self)]
}
}
impl IntoAsyncLua for LuaUserdata {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::from(self)]
}
}
impl<T: UserDataTrait> IntoAsyncLua for T {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::userdata(self)]
}
}
impl IntoAsyncLua for bool {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::boolean(self)]
}
}
macro_rules! impl_into_async_lua_int {
($($ty:ty),* $(,)?) => {
$(
impl IntoAsyncLua for $ty {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::integer(self as i64)]
}
}
)*
};
}
impl_into_async_lua_int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
macro_rules! impl_into_async_lua_float {
($($ty:ty),* $(,)?) => {
$(
impl IntoAsyncLua for $ty {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::float(self as f64)]
}
}
)*
};
}
impl_into_async_lua_float!(f32, f64);
impl IntoAsyncLua for String {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::String(self)]
}
}
impl IntoAsyncLua for &str {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
vec![AsyncReturnValue::string(self)]
}
}
impl<T: IntoAsyncLua> IntoAsyncLua for Option<T> {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
match self {
Some(value) => value.into_async_lua(),
None => vec![AsyncReturnValue::nil()],
}
}
}
impl<T: IntoAsyncLua> IntoAsyncLua for Vec<T> {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
let mut values = Vec::new();
for item in self {
values.extend(item.into_async_lua());
}
values
}
}
macro_rules! impl_into_async_lua_tuple {
($(($(($ty:ident, $value:ident)),+)),* $(,)?) => {
$(
impl<$($ty: IntoAsyncLua),+> IntoAsyncLua for ($($ty,)+) {
fn into_async_lua(self) -> Vec<AsyncReturnValue> {
let ($($value,)+) = self;
let mut values = Vec::new();
$(
values.extend($value.into_async_lua());
)+
values
}
}
)*
};
}
impl_into_async_lua_tuple!(
((A, a), (B, b)),
((A, a), (B, b), (C, c)),
((A, a), (B, b), (C, c), (D, d)),
((A, a), (B, b), (C, c), (D, d), (E, e)),
((A, a), (B, b), (C, c), (D, d), (E, e), (F, f)),
((A, a), (B, b), (C, c), (D, d), (E, e), (F, f), (G, g)),
(
(A, a),
(B, b),
(C, c),
(D, d),
(E, e),
(F, f),
(G, g),
(H, h)
)
);
pub type AsyncFuture = Pin<Box<dyn Future<Output = LuaResult<Vec<AsyncReturnValue>>>>>;
static ASYNC_SENTINEL_STORAGE: u8 = 0;
#[inline]
pub fn async_sentinel_value() -> LuaValue {
LuaValue::lightuserdata(&ASYNC_SENTINEL_STORAGE as *const u8 as *mut std::ffi::c_void)
}
#[inline]
pub fn is_async_sentinel(values: &[LuaValue]) -> bool {
if values.len() != 1 {
return false;
}
let v = &values[0];
v.ttislightuserdata()
&& v.pvalue() == &ASYNC_SENTINEL_STORAGE as *const u8 as *mut std::ffi::c_void
}
enum ResumeResult {
Finished(LuaResult<Vec<LuaValue>>),
AsyncYield(AsyncFuture),
NormalYield(Vec<LuaValue>),
}
fn materialize_values(vm: &mut LuaVM, values: Vec<AsyncReturnValue>) -> LuaResult<Vec<LuaValue>> {
let mut result = Vec::with_capacity(values.len());
for v in values {
result.push(materialize_single(vm, v)?);
}
Ok(result)
}
fn materialize_single(vm: &mut LuaVM, value: AsyncReturnValue) -> LuaResult<LuaValue> {
match value {
AsyncReturnValue::Value(lv) => Ok(lv),
AsyncReturnValue::String(s) => vm.create_string(&s),
AsyncReturnValue::UserData(ud) => vm.create_userdata(ud),
AsyncReturnValue::Table(entries) => {
let table = vm.create_table(0, entries.len())?;
for (k, v) in entries {
let key = materialize_single(vm, k)?;
let val = materialize_single(vm, v)?;
vm.raw_set(&table, key, val);
}
Ok(table)
}
}
}
pub struct AsyncThread {
thread_val: LuaValue,
vm: *mut LuaVM,
ref_id: RefId,
pending: Option<AsyncFuture>,
initial_args: Option<Vec<LuaValue>>,
}
impl AsyncThread {
pub(crate) fn new(thread_val: LuaValue, vm: *mut LuaVM, args: Vec<LuaValue>) -> Self {
let ref_id = {
let vm_ref = unsafe { &mut *vm };
let lua_ref = vm_ref.create_ref(thread_val);
lua_ref.ref_id().unwrap_or(0)
};
AsyncThread {
thread_val,
vm,
ref_id,
pending: None,
initial_args: Some(args),
}
}
fn do_resume(&mut self, args: Vec<LuaValue>) -> ResumeResult {
let thread_state = match self.thread_val.as_thread_mut() {
Some(state) => state,
None => {
return ResumeResult::Finished(Err(
unsafe { &mut *self.vm }.error("AsyncThread: invalid thread value".to_string())
));
}
};
match thread_state.resume(args) {
Ok((true, results)) => {
ResumeResult::Finished(Ok(results))
}
Ok((false, values)) => {
if is_async_sentinel(&values) {
match thread_state.take_pending_future() {
Some(fut) => ResumeResult::AsyncYield(fut),
None => {
ResumeResult::Finished(Err(unsafe { &mut *self.vm }
.error("async yield without pending future".to_string())))
}
}
} else {
ResumeResult::NormalYield(values)
}
}
Err(e) => ResumeResult::Finished(Err(e)),
}
}
fn poll_pending(&mut self, cx: &mut Context<'_>) -> Poll<LuaResult<Vec<LuaValue>>> {
loop {
if let Some(ref mut fut) = self.pending {
match fut.as_mut().poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(result) => {
self.pending = None;
let resume_args = match result {
Ok(async_values) => {
let vm = unsafe { &mut *self.vm };
match materialize_values(vm, async_values) {
Ok(values) => values,
Err(e) => return Poll::Ready(Err(e)),
}
}
Err(e) => {
return Poll::Ready(Err(e));
}
};
match self.do_resume(resume_args) {
ResumeResult::Finished(r) => return Poll::Ready(r),
ResumeResult::AsyncYield(fut) => {
self.pending = Some(fut);
continue; }
ResumeResult::NormalYield(_vals) => {
cx.waker().wake_by_ref();
return Poll::Pending;
}
}
}
}
} else {
return Poll::Ready(Err(unsafe { &mut *self.vm }
.error("AsyncThread: no pending future to poll".to_string())));
}
}
}
}
impl Future for AsyncThread {
type Output = LuaResult<Vec<LuaValue>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if this.pending.is_some() {
return this.poll_pending(cx);
}
let args = this.initial_args.take().unwrap_or_default();
match this.do_resume(args) {
ResumeResult::Finished(r) => Poll::Ready(r),
ResumeResult::AsyncYield(fut) => {
this.pending = Some(fut);
this.poll_pending(cx)
}
ResumeResult::NormalYield(_vals) => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
impl Drop for AsyncThread {
fn drop(&mut self) {
if self.ref_id > 0 {
let vm = unsafe { &mut *self.vm };
vm.release_ref_id(self.ref_id);
}
}
}
pub fn wrap_async_function<F, Fut>(
f: F,
) -> impl Fn(&mut crate::lua_vm::LuaState) -> LuaResult<usize> + 'static
where
F: Fn(Vec<LuaValue>) -> Fut + 'static,
Fut: Future<Output = LuaResult<Vec<AsyncReturnValue>>> + 'static,
{
move |state: &mut crate::lua_vm::LuaState| {
let args = state.get_args();
let future = f(args);
state.set_pending_future(Box::pin(future));
state.do_yield(vec![async_sentinel_value()])?;
Ok(0)
}
}
pub(crate) const ASYNC_CALL_RUNNER: &str = "\
local f = ...\n\
local args = table.pack(coroutine.yield())\n\
while true do\n\
args = table.pack(coroutine.yield(pcall(f, table.unpack(args, 1, args.n))))\n\
end";
pub struct AsyncCallHandle {
thread_val: LuaValue,
vm: *mut LuaVM,
ref_id: RefId,
alive: bool,
}
impl AsyncCallHandle {
pub(crate) fn new(thread_val: LuaValue, vm: *mut LuaVM, func: LuaValue) -> LuaResult<Self> {
let ref_id = {
let vm_ref = unsafe { &mut *vm };
let lua_ref = vm_ref.create_ref(thread_val);
lua_ref.ref_id().unwrap_or(0)
};
let handle = AsyncCallHandle {
thread_val,
vm,
ref_id,
alive: true,
};
let thread_state = handle
.thread_val
.as_thread_mut()
.ok_or_else(|| unsafe { &mut *vm }.error("invalid thread value".to_string()))?;
let (finished, _) = thread_state.resume(vec![func])?;
if finished {
return Err(
unsafe { &mut *vm }.error("runner coroutine finished during init".to_string())
);
}
Ok(handle)
}
pub fn is_alive(&self) -> bool {
self.alive
}
fn do_resume(&mut self, args: Vec<LuaValue>) -> ResumeResult {
let thread_state = match self.thread_val.as_thread_mut() {
Some(state) => state,
None => {
return ResumeResult::Finished(Err(
unsafe { &mut *self.vm }.error("invalid thread value".to_string())
));
}
};
match thread_state.resume(args) {
Ok((true, results)) => ResumeResult::Finished(Ok(results)),
Ok((false, values)) => {
if is_async_sentinel(&values) {
match thread_state.take_pending_future() {
Some(fut) => ResumeResult::AsyncYield(fut),
None => ResumeResult::Finished(Err(unsafe { &mut *self.vm }
.error("async yield without pending future".to_string()))),
}
} else {
ResumeResult::NormalYield(values)
}
}
Err(e) => ResumeResult::Finished(Err(e)),
}
}
pub async fn call(&mut self, args: Vec<LuaValue>) -> LuaResult<Vec<LuaValue>> {
if !self.alive {
return Err(
unsafe { &mut *self.vm }.error("async call handle is no longer alive".to_string())
);
}
let mut resume_args = args;
loop {
match self.do_resume(resume_args) {
ResumeResult::Finished(result) => {
self.alive = false;
match result {
Ok(_) => {
return Err(unsafe { &mut *self.vm }
.error("runner coroutine finished unexpectedly".to_string()));
}
Err(e) => return Err(e),
}
}
ResumeResult::AsyncYield(fut) => match fut.await {
Ok(async_values) => {
let vm = unsafe { &mut *self.vm };
resume_args = materialize_values(vm, async_values)?;
}
Err(e) => {
self.alive = false;
return Err(e);
}
},
ResumeResult::NormalYield(values) => {
let ok = values.first().and_then(|v| v.as_boolean()).unwrap_or(false);
if ok {
return Ok(values[1..].to_vec());
} else {
let err_msg = values
.get(1)
.and_then(|v| v.as_str())
.unwrap_or("unknown error")
.to_string();
return Err(unsafe { &mut *self.vm }.error(err_msg));
}
}
}
}
}
}
impl Drop for AsyncCallHandle {
fn drop(&mut self) {
if self.ref_id > 0 {
let vm = unsafe { &mut *self.vm };
vm.release_ref_id(self.ref_id);
}
}
}