Skip to content

Commit 7d2fc11

Browse files
committed
Map implicit async threads to their root owner
`Lua::current_thread()` now returns a stable handle instead of a temporary coroutine. Closes #706
1 parent ed4376d commit 7d2fc11

6 files changed

Lines changed: 56 additions & 1 deletion

File tree

src/function.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ impl Function {
252252
lua.create_recycled_thread(self).and_then(|th| {
253253
let mut th = th.into_async(args)?;
254254
th.set_recyclable(true);
255+
lua.update_thread_ownership(th.thread(), Some(lua.state()));
255256
Ok(th)
256257
})
257258
})

src/state.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,18 @@ impl Lua {
17641764
pub fn current_thread(&self) -> Thread {
17651765
let lua = self.lock();
17661766
let state = lua.state();
1767+
let extra = lua.extra.get();
17671768
unsafe {
1769+
// If this thread is implicit (created by `call_async`), return the root user-owned thread
1770+
// instead.
1771+
#[cfg(feature = "async")]
1772+
if let Some(&owner) = (*extra).thread_ownership_map.get(&state) {
1773+
assert_stack(owner, 1);
1774+
ffi::lua_pushthread(owner);
1775+
ffi::lua_xmove(owner, lua.ref_thread(), 1);
1776+
return Thread(lua.pop_ref_thread(), owner);
1777+
}
1778+
17681779
let _sg = StackGuard::new(state);
17691780
assert_stack(state, 1);
17701781
ffi::lua_pushthread(state);

src/state/extra.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ pub(crate) struct ExtraData {
6666
// Pool of `Thread`s (coroutines) for async execution
6767
#[cfg(feature = "async")]
6868
pub(super) thread_pool: Vec<crate::types::ValueRefIndex>,
69+
// Map for implicit threads to root user-owned Thread
70+
#[cfg(feature = "async")]
71+
pub(super) thread_ownership_map: FxHashMap<*mut ffi::lua_State, *mut ffi::lua_State>,
6972

7073
// Address of `WrappedFailure` metatable
7174
pub(super) wrapped_failure_mt_ptr: *const c_void,
@@ -174,6 +177,8 @@ impl ExtraData {
174177
wrapped_failure_top: 0,
175178
#[cfg(feature = "async")]
176179
thread_pool: Vec::new(),
180+
#[cfg(feature = "async")]
181+
thread_ownership_map: FxHashMap::default(),
177182
wrapped_failure_mt_ptr,
178183
#[cfg(feature = "async")]
179184
waker: NonNull::from(noop_waker_ref()),

src/state/raw.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,24 @@ impl RawLua {
693693
self.create_thread(func)
694694
}
695695

696+
/// Updates the ownership of the given implicit thread to the root user-owned thread.
697+
///
698+
/// If `owner` is `None`, the thread is removed from the ownership map.
699+
#[cfg(feature = "async")]
700+
pub(crate) unsafe fn update_thread_ownership(&self, th: &Thread, owner: Option<*mut ffi::lua_State>) {
701+
let extra = &mut *self.extra.get();
702+
let th_state = th.state();
703+
match owner {
704+
Some(owner) => {
705+
let new_owner = (extra.thread_ownership_map).get(&owner).copied().unwrap_or(owner);
706+
extra.thread_ownership_map.insert(th_state, new_owner);
707+
}
708+
None => {
709+
extra.thread_ownership_map.remove(&th_state);
710+
}
711+
}
712+
}
713+
696714
/// Returns the thread to the pool for later use.
697715
#[cfg(feature = "async")]
698716
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {

src/thread.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,11 @@ impl<R> AsyncThread<R> {
689689
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
690690
self.recycle = recyclable;
691691
}
692+
693+
#[inline(always)]
694+
pub(crate) fn thread(&self) -> &Thread {
695+
&self.thread
696+
}
692697
}
693698

694699
#[cfg(feature = "async")]
@@ -712,6 +717,7 @@ impl<R> Drop for AsyncThread<R> {
712717
if self.thread.reset_inner(status).is_ok() {
713718
lua.recycle_thread(&mut self.thread);
714719
}
720+
lua.update_thread_ownership(&self.thread, None);
715721
}
716722
}
717723
}

tests/async.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use futures_util::stream::TryStreamExt;
77
use tokio::sync::Mutex;
88

99
use mlua::{
10-
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
10+
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, Thread, UserData,
1111
UserDataMethods, UserDataRef, Value,
1212
};
1313

@@ -718,3 +718,17 @@ fn test_async_yield_with() -> Result<()> {
718718

719719
Ok(())
720720
}
721+
722+
#[tokio::test]
723+
async fn test_async_current_thread() -> Result<()> {
724+
let lua = Lua::new();
725+
726+
let get_inner_thread = lua.create_async_function(move |lua, ()| async move {
727+
let f = lua.create_async_function(move |lua, ()| async move { Ok(lua.current_thread()) })?;
728+
f.call_async::<Thread>(()).await
729+
})?;
730+
let inner_thread = get_inner_thread.call_async::<Thread>(()).await?;
731+
assert_eq!(inner_thread, lua.current_thread());
732+
733+
Ok(())
734+
}

0 commit comments

Comments
 (0)