1use std::os::raw::{c_int, c_void};
2
3use crate::error::{Error, Result};
4#[allow(unused)]
5use crate::lua::Lua;
6use crate::types::LuaRef;
7use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
8use crate::value::{FromLuaMulti, IntoLuaMulti};
9
10#[cfg(not(feature = "luau"))]
11use crate::{
12 hook::{Debug, HookTriggers},
13 types::MaybeSend,
14};
15
16#[cfg(feature = "async")]
17use {
18 crate::value::MultiValue,
19 futures_util::stream::Stream,
20 std::{
21 future::Future,
22 marker::PhantomData,
23 pin::Pin,
24 ptr::NonNull,
25 task::{Context, Poll, Waker},
26 },
27};
28
29#[derive(Debug, Copy, Clone, Eq, PartialEq)]
31pub enum ThreadStatus {
32 Resumable,
38 Unresumable,
40 Error,
42}
43
44#[derive(Clone, Debug)]
46pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
47
48#[cfg(feature = "unstable")]
56#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
57#[derive(Clone, Debug)]
58pub struct OwnedThread(
59 pub(crate) crate::types::LuaOwnedRef,
60 pub(crate) *mut ffi::lua_State,
61);
62
63#[cfg(feature = "unstable")]
64impl OwnedThread {
65 #[cfg_attr(feature = "send", allow(unused))]
67 pub const fn to_ref(&self) -> Thread {
68 Thread(self.0.to_ref(), self.1)
69 }
70}
71
72#[cfg(feature = "async")]
79#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
80#[must_use = "futures do nothing unless you `.await` or poll them"]
81pub struct AsyncThread<'lua, R> {
82 thread: Thread<'lua>,
83 init_args: Option<Result<MultiValue<'lua>>>,
84 ret: PhantomData<R>,
85 recycle: bool,
86}
87
88impl<'lua> Thread<'lua> {
89 #[inline(always)]
90 pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
91 let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
92 Thread(r#ref, state)
93 }
94
95 const fn state(&self) -> *mut ffi::lua_State {
96 self.1
97 }
98
99 pub fn resume<A, R>(&self, args: A) -> Result<R>
141 where
142 A: IntoLuaMulti<'lua>,
143 R: FromLuaMulti<'lua>,
144 {
145 if self.status() != ThreadStatus::Resumable {
146 return Err(Error::CoroutineInactive);
147 }
148
149 let lua = self.0.lua;
150 let state = lua.state();
151 let thread_state = self.state();
152 unsafe {
153 let _sg = StackGuard::new(state);
154 let _thread_sg = StackGuard::with_top(thread_state, 0);
155
156 let nresults = self.resume_inner(args)?;
157 check_stack(state, nresults + 1)?;
158 ffi::lua_xmove(thread_state, state, nresults);
159
160 R::from_stack_multi(nresults, lua)
161 }
162 }
163
164 unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
168 let lua = self.0.lua;
169 let state = lua.state();
170 let thread_state = self.state();
171
172 let nargs = args.push_into_stack_multi(lua)?;
173 if nargs > 0 {
174 check_stack(thread_state, nargs)?;
175 ffi::lua_xmove(state, thread_state, nargs);
176 }
177
178 let mut nresults = 0;
179 let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
180 if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
181 if ret == ffi::LUA_ERRMEM {
182 return Err(pop_error(thread_state, ret));
184 }
185 check_stack(state, 3)?;
186 protect_lua!(state, 0, 1, |state| error_traceback_thread(
187 state,
188 thread_state
189 ))?;
190 return Err(pop_error(state, ret));
191 }
192
193 Ok(nresults)
194 }
195
196 pub fn status(&self) -> ThreadStatus {
198 let thread_state = self.state();
199 if thread_state == self.0.lua.state() {
200 return ThreadStatus::Unresumable;
202 }
203 unsafe {
204 let status = ffi::lua_status(thread_state);
205 if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
206 ThreadStatus::Error
207 } else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
208 ThreadStatus::Resumable
209 } else {
210 ThreadStatus::Unresumable
211 }
212 }
213 }
214
215 #[cfg(not(feature = "luau"))]
220 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
221 pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
222 where
223 F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
224 {
225 let lua = self.0.lua;
226 unsafe {
227 lua.set_thread_hook(self.state(), triggers, callback);
228 }
229 }
230
231 #[cfg(any(feature = "lua54", feature = "luau"))]
246 #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
247 pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
248 let lua = self.0.lua;
249 let thread_state = self.state();
250 if thread_state == lua.state() {
251 return Err(Error::runtime("cannot reset a running thread"));
252 }
253 unsafe {
254 #[cfg(all(feature = "lua54", not(feature = "vendored")))]
255 let status = ffi::lua_resetthread(thread_state);
256 #[cfg(all(feature = "lua54", feature = "vendored"))]
257 let status = ffi::lua_closethread(thread_state, lua.state());
258 #[cfg(feature = "lua54")]
259 if status != ffi::LUA_OK {
260 return Err(pop_error(thread_state, status));
261 }
262 #[cfg(feature = "luau")]
263 ffi::lua_resetthread(thread_state);
264
265 ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
267
268 #[cfg(feature = "luau")]
269 {
270 ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
272 ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
273 }
274
275 Ok(())
276 }
277 }
278
279 #[cfg(feature = "async")]
325 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
326 pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
327 where
328 A: IntoLuaMulti<'lua>,
329 R: FromLuaMulti<'lua>,
330 {
331 let args = args.into_lua_multi(self.0.lua);
332 AsyncThread {
333 thread: self,
334 init_args: Some(args),
335 ret: PhantomData,
336 recycle: false,
337 }
338 }
339
340 #[cfg(any(feature = "luau", docsrs))]
372 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
373 #[doc(hidden)]
374 pub fn sandbox(&self) -> Result<()> {
375 let lua = self.0.lua;
376 let state = lua.state();
377 let thread_state = self.state();
378 unsafe {
379 check_stack(thread_state, 3)?;
380 check_stack(state, 3)?;
381 protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
382 }
383 }
384
385 #[inline]
391 pub fn to_pointer(&self) -> *const c_void {
392 self.0.to_pointer()
393 }
394
395 #[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
397 #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
398 #[inline]
399 pub fn into_owned(self) -> OwnedThread {
400 OwnedThread(self.0.into_owned(), self.1)
401 }
402}
403
404impl<'lua> PartialEq for Thread<'lua> {
405 fn eq(&self, other: &Self) -> bool {
406 self.0 == other.0
407 }
408}
409
410#[cfg(feature = "unstable")]
412impl OwnedThread {
413 pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
417 where
418 A: IntoLuaMulti<'lua>,
419 R: FromLuaMulti<'lua>,
420 {
421 self.to_ref().resume(args)
422 }
423
424 pub fn status(&self) -> ThreadStatus {
426 self.to_ref().status()
427 }
428}
429
430#[cfg(feature = "async")]
431impl<'lua, R> AsyncThread<'lua, R> {
432 #[inline]
433 pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
434 self.recycle = recyclable;
435 }
436}
437
438#[cfg(feature = "async")]
439#[cfg(any(feature = "lua54", feature = "luau"))]
440impl<'lua, R> Drop for AsyncThread<'lua, R> {
441 fn drop(&mut self) {
442 if self.recycle {
443 unsafe {
444 let lua = self.thread.0.lua;
445 if !lua.recycle_thread(&mut self.thread) {
447 #[cfg(feature = "lua54")]
448 if self.thread.status() == ThreadStatus::Error {
449 #[cfg(not(feature = "vendored"))]
450 ffi::lua_resetthread(self.thread.state());
451 #[cfg(feature = "vendored")]
452 ffi::lua_closethread(self.thread.state(), lua.state());
453 }
454 }
455 }
456 }
457 }
458}
459
460#[cfg(feature = "async")]
461impl<'lua, R> Stream for AsyncThread<'lua, R>
462where
463 R: FromLuaMulti<'lua>,
464{
465 type Item = Result<R>;
466
467 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
468 if self.thread.status() != ThreadStatus::Resumable {
469 return Poll::Ready(None);
470 }
471
472 let lua = self.thread.0.lua;
473 let state = lua.state();
474 let thread_state = self.thread.state();
475 unsafe {
476 let _sg = StackGuard::new(state);
477 let _thread_sg = StackGuard::with_top(thread_state, 0);
478 let _wg = WakerGuard::new(lua, cx.waker());
479
480 let this = self.get_unchecked_mut();
482 let nresults = if let Some(args) = this.init_args.take() {
483 this.thread.resume_inner(args?)?
484 } else {
485 this.thread.resume_inner(())?
486 };
487
488 if nresults == 1 && is_poll_pending(thread_state) {
489 return Poll::Pending;
490 }
491
492 check_stack(state, nresults + 1)?;
493 ffi::lua_xmove(thread_state, state, nresults);
494
495 cx.waker().wake_by_ref();
496 Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
497 }
498 }
499}
500
501#[cfg(feature = "async")]
502impl<'lua, R> Future for AsyncThread<'lua, R>
503where
504 R: FromLuaMulti<'lua>,
505{
506 type Output = Result<R>;
507
508 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
509 if self.thread.status() != ThreadStatus::Resumable {
510 return Poll::Ready(Err(Error::CoroutineInactive));
511 }
512
513 let lua = self.thread.0.lua;
514 let state = lua.state();
515 let thread_state = self.thread.state();
516 unsafe {
517 let _sg = StackGuard::new(state);
518 let _thread_sg = StackGuard::with_top(thread_state, 0);
519 let _wg = WakerGuard::new(lua, cx.waker());
520
521 let this = self.get_unchecked_mut();
523 let nresults = if let Some(args) = this.init_args.take() {
524 this.thread.resume_inner(args?)?
525 } else {
526 this.thread.resume_inner(())?
527 };
528
529 if nresults == 1 && is_poll_pending(thread_state) {
530 return Poll::Pending;
531 }
532
533 if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
534 cx.waker().wake_by_ref();
536 return Poll::Pending;
537 }
538
539 check_stack(state, nresults + 1)?;
540 ffi::lua_xmove(thread_state, state, nresults);
541
542 Poll::Ready(R::from_stack_multi(nresults, lua))
543 }
544 }
545}
546
547#[cfg(feature = "async")]
548#[inline(always)]
549unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
550 ffi::lua_tolightuserdata(state, -1) == Lua::poll_pending().0
551}
552
553#[cfg(feature = "async")]
554struct WakerGuard<'lua, 'a> {
555 lua: &'lua Lua,
556 prev: NonNull<Waker>,
557 _phantom: PhantomData<&'a ()>,
558}
559
560#[cfg(feature = "async")]
561impl<'lua, 'a> WakerGuard<'lua, 'a> {
562 #[inline]
563 pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
564 let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
565 Ok(WakerGuard {
566 lua,
567 prev,
568 _phantom: PhantomData,
569 })
570 }
571}
572
573#[cfg(feature = "async")]
574impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
575 fn drop(&mut self) {
576 unsafe { self.lua.set_waker(self.prev) };
577 }
578}
579
580#[cfg(test)]
581mod assertions {
582 use super::*;
583
584 static_assertions::assert_not_impl_any!(Thread: Send);
585}