Skip to content

Commit 3aaf510

Browse files
committed
Add yielding support.
1 parent cd4091f commit 3aaf510

File tree

5 files changed

+51
-1
lines changed

5 files changed

+51
-1
lines changed

src/error.rs

+10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ pub enum Error {
205205
/// Underlying error.
206206
cause: Arc<Error>,
207207
},
208+
// Yield.
209+
//
210+
// Not an error.
211+
// Returning `Err(Yielding)` from a Rust callback will yield with no values.
212+
// See `Lua::yield(args: impl FromLuaMulti) -> Result<Infallible, Error>` for more info.
213+
// If it cannot yield, it will raise an error.
214+
Yielding,
208215
}
209216

210217
/// A specialized `Result` type used by `mlua`'s API.
@@ -321,6 +328,9 @@ impl fmt::Display for Error {
321328
Error::WithContext { context, cause } => {
322329
writeln!(fmt, "{context}")?;
323330
write!(fmt, "{cause}")
331+
},
332+
Error::Yielding => {
333+
write!(fmt, "yield across Rust/Lua boundary")
324334
}
325335
}
326336
}

src/state.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::any::TypeId;
22
use std::cell::{BorrowError, BorrowMutError, RefCell};
3+
use std::convert::Infallible;
34
use std::marker::PhantomData;
45
use std::ops::Deref;
56
use std::os::raw::c_int;
@@ -1966,6 +1967,20 @@ impl Lua {
19661967
pub(crate) unsafe fn raw_lua(&self) -> &RawLua {
19671968
&*self.raw.data_ptr()
19681969
}
1970+
/// Yields arguments
1971+
///
1972+
/// If this function cannot yield, it will raise a runtime error. It yields by using
1973+
/// the Error::Yielding error to rapidly exit all of the try blocks.
1974+
pub fn yield_args(&self, args: impl IntoLuaMulti) -> Result<Infallible> {
1975+
let raw = self.lock();
1976+
if !raw.is_yieldable() {
1977+
return Err(Error::runtime("cannot yield across Rust/Lua boundary."))
1978+
}
1979+
unsafe {
1980+
raw.extra.get().as_mut().unwrap_unchecked().yielded_values = args.into_lua_multi(self)?;
1981+
}
1982+
Err(Error::Yielding)
1983+
}
19691984
}
19701985

19711986
impl WeakLua {

src/state/extra.rs

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, Wrapp
1818

1919
#[cfg(any(feature = "luau", doc))]
2020
use crate::chunk::Compiler;
21+
use crate::MultiValue;
2122

2223
#[cfg(feature = "async")]
2324
use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
@@ -87,6 +88,9 @@ pub(crate) struct ExtraData {
8788
pub(super) compiler: Option<Compiler>,
8889
#[cfg(feature = "luau-jit")]
8990
pub(super) enable_jit: bool,
91+
92+
// Values currently being yielded from Lua.yield()
93+
pub(super) yielded_values: MultiValue,
9094
}
9195

9296
impl Drop for ExtraData {
@@ -182,6 +186,7 @@ impl ExtraData {
182186
compiler: None,
183187
#[cfg(feature = "luau-jit")]
184188
enable_jit: true,
189+
yielded_values: MultiValue::default(),
185190
}));
186191

187192
// Store it in the registry

src/state/raw.rs

+5
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ impl RawLua {
342342
}
343343
}
344344

345+
#[inline]
346+
pub(crate) fn is_yieldable(&self) -> bool {
347+
unsafe { ffi::lua_isyieldable(self.state()) != 0 }
348+
}
349+
345350
pub(crate) unsafe fn load_chunk_inner(
346351
&self,
347352
state: *mut ffi::lua_State,

src/state/util.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::mem::take;
12
use std::os::raw::c_int;
23
use std::panic::{catch_unwind, AssertUnwindSafe};
34
use std::ptr;
@@ -6,6 +7,7 @@ use std::sync::Arc;
67
use crate::error::{Error, Result};
78
use crate::state::{ExtraData, RawLua};
89
use crate::util::{self, get_internal_metatable, WrappedFailure};
10+
use crate::IntoLuaMulti;
911

1012
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
1113

@@ -107,7 +109,20 @@ where
107109
prealloc_failure.release(state, extra);
108110
r
109111
}
110-
Ok(Err(err)) => {
112+
Ok(Err(mut err)) => {
113+
if let Error::Yielding = err {
114+
let raw = extra.as_ref().unwrap_unchecked().raw_lua();
115+
let values = take(&mut extra.as_mut().unwrap_unchecked().yielded_values);
116+
match values.push_into_stack_multi(raw) {
117+
Ok(nargs) => {
118+
ffi::lua_yield(state, nargs);
119+
unreachable!()
120+
},
121+
Err(new_err) => {
122+
err = new_err;
123+
}
124+
}
125+
}
111126
let wrapped_error = prealloc_failure.r#use(state, extra);
112127

113128
// Build `CallbackError` with traceback

0 commit comments

Comments
 (0)