1use std::fmt;
39use std::os::raw::{c_int, c_void};
40
41use crate::error::{Error, Result};
42use crate::function::Function;
43use crate::state::RawLua;
44use crate::traits::{FromLuaMulti, IntoLuaMulti};
45use crate::types::{LuaType, ValueRef};
46use crate::util::{StackGuard, check_stack, error_traceback_thread, pop_error};
47
48#[cfg(not(feature = "luau"))]
49use crate::{
50 debug::{Debug, HookTriggers},
51 types::HookKind,
52};
53
54#[cfg(feature = "async")]
55use {
56 futures_util::stream::Stream,
57 std::{
58 future::Future,
59 marker::PhantomData,
60 pin::Pin,
61 ptr::NonNull,
62 task::{Context, Poll, Waker},
63 },
64};
65
66#[derive(Clone, Copy, Debug, Default)]
68#[non_exhaustive]
69pub struct ThreadTriggers {
70 pub on_create: bool,
75 pub on_resume: bool,
78 pub on_yield: bool,
81}
82
83impl ThreadTriggers {
84 pub const ON_CREATE: Self = Self::new().on_create();
86
87 pub const ON_RESUME: Self = Self::new().on_resume();
89
90 pub const ON_YIELD: Self = Self::new().on_yield();
92
93 pub const fn new() -> Self {
95 Self {
96 on_create: false,
97 on_resume: false,
98 on_yield: false,
99 }
100 }
101
102 #[must_use]
104 pub const fn on_create(mut self) -> Self {
105 self.on_create = true;
106 self
107 }
108
109 #[must_use]
111 pub const fn on_resume(mut self) -> Self {
112 self.on_resume = true;
113 self
114 }
115
116 #[must_use]
118 pub const fn on_yield(mut self) -> Self {
119 self.on_yield = true;
120 self
121 }
122}
123
124impl std::ops::BitOr for ThreadTriggers {
125 type Output = Self;
126
127 fn bitor(mut self, rhs: Self) -> Self::Output {
128 self.on_create |= rhs.on_create;
129 self.on_resume |= rhs.on_resume;
130 self.on_yield |= rhs.on_yield;
131 self
132 }
133}
134
135impl std::ops::BitOrAssign for ThreadTriggers {
136 fn bitor_assign(&mut self, rhs: Self) {
137 *self = *self | rhs;
138 }
139}
140
141#[derive(Debug, Clone)]
143#[non_exhaustive]
144pub enum ThreadEvent {
145 Create(Thread),
147 Resume(Thread),
149 Yield(Thread),
151}
152
153#[derive(Debug, Copy, Clone, Eq, PartialEq)]
155pub enum ThreadStatus {
156 Resumable,
160 Running,
162 Normal,
167 Finished,
169 Error,
171}
172
173#[derive(Clone, Copy)]
178enum ThreadStatusInner {
179 New(c_int),
180 Running,
181 Normal,
182 Yielded(c_int),
183 Finished,
184 Error,
185}
186
187impl ThreadStatusInner {
188 #[inline(always)]
189 fn is_yielded(self) -> bool {
190 matches!(self, ThreadStatusInner::Yielded(_))
191 }
192}
193
194#[derive(Clone, PartialEq)]
196pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
197
198#[cfg(feature = "send")]
199unsafe impl Send for Thread {}
200#[cfg(feature = "send")]
201unsafe impl Sync for Thread {}
202
203#[cfg(feature = "async")]
208#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
209#[must_use = "futures do nothing unless you `.await` or poll them"]
210pub struct AsyncThread<R> {
211 thread: Thread,
212 ret: PhantomData<fn() -> R>,
213 recycle: bool,
214}
215
216pub(crate) struct ThreadEventGuard<'a> {
217 lua: &'a RawLua,
218 prev_state: *mut ffi::lua_State,
219}
220
221impl<'a> ThreadEventGuard<'a> {
222 #[inline]
223 pub(crate) unsafe fn new(lua: &'a RawLua, thread_state: *mut ffi::lua_State) -> Self {
224 let guard = ThreadEventGuard {
225 lua,
226 prev_state: lua.thread_event_state(),
227 };
228 lua.set_thread_event_state(thread_state);
229 guard
230 }
231}
232
233impl Drop for ThreadEventGuard<'_> {
234 #[inline]
235 fn drop(&mut self) {
236 unsafe { self.lua.set_thread_event_state(self.prev_state) };
237 }
238}
239
240#[inline]
241fn check_thread_reentrancy(thread_state: *mut ffi::lua_State, lua: &RawLua) -> Result<()> {
242 if thread_state == unsafe { lua.thread_event_state() } {
243 let err = "cannot resume or reset a thread from within its own event callback";
244 return Err(Error::runtime(err));
245 }
246 Ok(())
247}
248
249#[inline]
250unsafe fn exec_thread_event(
251 lua: &RawLua,
252 enabled: bool,
253 thread_state: *mut ffi::lua_State,
254 event: impl FnOnce() -> ThreadEvent,
255) -> Result<bool> {
256 if enabled
257 && lua.thread_event_state().is_null()
258 && let Some(cb) = lua.thread_event_callback()
259 {
260 let _guard = ThreadEventGuard::new(lua, thread_state);
261 cb(lua.lua(), event())?;
262 return Ok(true);
263 }
264 Ok(false)
265}
266
267impl Thread {
268 #[inline(always)]
272 pub fn state(&self) -> *mut ffi::lua_State {
273 self.1
274 }
275
276 pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
321 where
322 R: FromLuaMulti,
323 {
324 let lua = self.0.lua.lock();
325 check_thread_reentrancy(self.state(), &lua)?;
326 let (mut pushed_nargs, mut hook_yielded) = self.resumable_state(&lua)?;
327
328 let state = lua.state();
329 let thread_state = self.state();
330 unsafe {
331 let _sg = StackGuard::new(state);
332
333 let on_resume = lua.thread_event_triggers().on_resume;
335 if exec_thread_event(&lua, on_resume, thread_state, || {
336 ThreadEvent::Resume(self.clone())
337 })? {
338 (pushed_nargs, hook_yielded) = self.resumable_state(&lua)?;
339 }
340
341 if !hook_yielded {
342 let nargs = args.push_into_stack_multi(&lua)?;
343 if nargs > 0 {
344 check_stack(thread_state, nargs)?;
345 ffi::lua_xmove(state, thread_state, nargs);
346 pushed_nargs += nargs;
347 }
348 }
349
350 let mut thread_sg = StackGuard::with_top(thread_state, 0);
351 let (status, nresults) = self.resume_inner(&lua, pushed_nargs)?;
352 if status.is_yielded() && self.is_hook_yielded(&lua) {
353 debug_assert_eq!(nresults, 0);
354 thread_sg.keep(ffi::lua_gettop(thread_state));
355 }
356
357 check_stack(state, nresults + 1)?;
358 ffi::lua_xmove(thread_state, state, nresults);
359
360 let on_yield = lua.thread_event_triggers().on_yield && status.is_yielded();
362 exec_thread_event(&lua, on_yield, thread_state, || ThreadEvent::Yield(self.clone()))?;
363
364 R::from_stack_multi(nresults, &lua)
365 }
366 }
367
368 #[cfg(any(feature = "luau", doc))]
372 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
373 pub fn resume_error<R>(&self, error: impl crate::IntoLua) -> Result<R>
374 where
375 R: FromLuaMulti,
376 {
377 let lua = self.0.lua.lock();
378 check_thread_reentrancy(self.state(), &lua)?;
379 match self.status_inner(&lua) {
380 ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_) => {}
381 _ => return Err(Error::CoroutineUnresumable),
382 };
383
384 let state = lua.state();
385 let thread_state = self.state();
386 unsafe {
387 let _sg = StackGuard::new(state);
388
389 let on_resume = lua.thread_event_triggers().on_resume;
391 exec_thread_event(&lua, on_resume, thread_state, || {
392 ThreadEvent::Resume(self.clone())
393 })?;
394
395 check_stack(state, 1)?;
396 error.push_into_stack(&lua)?;
397 check_stack(thread_state, 1)?;
398 ffi::lua_xmove(state, thread_state, 1);
399
400 let _thread_sg = StackGuard::with_top(thread_state, 0);
401 let (status, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
402
403 check_stack(state, nresults + 1)?;
404 ffi::lua_xmove(thread_state, state, nresults);
405
406 let on_yield = lua.thread_event_triggers().on_yield && status.is_yielded();
408 exec_thread_event(&lua, on_yield, thread_state, || ThreadEvent::Yield(self.clone()))?;
409
410 R::from_stack_multi(nresults, &lua)
411 }
412 }
413
414 unsafe fn resume_inner(&self, lua: &RawLua, nargs: c_int) -> Result<(ThreadStatusInner, c_int)> {
418 let state = lua.state();
419 let thread_state = self.state();
420 let mut nresults = 0;
421 #[cfg(not(feature = "luau"))]
422 let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
423 #[cfg(feature = "luau")]
424 let ret = ffi::lua_resumex(thread_state, state, nargs, &mut nresults as *mut c_int);
425 match ret {
426 ffi::LUA_OK => Ok((ThreadStatusInner::Finished, nresults)),
427 ffi::LUA_YIELD => Ok((ThreadStatusInner::Yielded(0), nresults)),
428 ffi::LUA_ERRMEM => {
429 Err(pop_error(thread_state, ret))
431 }
432 _ => {
433 check_stack(state, 3)?;
434 protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
435 Err(pop_error(state, ret))
436 }
437 }
438 }
439
440 pub fn status(&self) -> ThreadStatus {
442 match self.status_inner(&self.0.lua.lock()) {
443 ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_) => ThreadStatus::Resumable,
444 ThreadStatusInner::Running => ThreadStatus::Running,
445 ThreadStatusInner::Normal => ThreadStatus::Normal,
446 ThreadStatusInner::Finished => ThreadStatus::Finished,
447 ThreadStatusInner::Error => ThreadStatus::Error,
448 }
449 }
450
451 fn status_inner(&self, lua: &RawLua) -> ThreadStatusInner {
453 let thread_state = self.state();
454 if thread_state == lua.state() {
455 return ThreadStatusInner::Running;
457 }
458 let status = unsafe { ffi::lua_status(thread_state) };
459 let top = unsafe { ffi::lua_gettop(thread_state) };
460 match status {
461 ffi::LUA_YIELD => ThreadStatusInner::Yielded(top),
462 ffi::LUA_OK => {
463 let mut ar = const { unsafe { std::mem::zeroed::<ffi::lua_Debug>() } };
466 #[cfg(not(feature = "luau"))]
467 let has_frames = unsafe { ffi::lua_getstack(thread_state, 0, &mut ar) != 0 };
468 #[cfg(feature = "luau")]
469 let has_frames = unsafe { ffi::lua_getinfo(thread_state, 0, cstr!(""), &mut ar) != 0 };
470 if has_frames {
471 ThreadStatusInner::Normal
472 } else if top > 0 {
473 ThreadStatusInner::New(top - 1)
474 } else {
475 ThreadStatusInner::Finished
476 }
477 }
478 _ => ThreadStatusInner::Error,
479 }
480 }
481
482 #[inline]
484 fn resumable_state(&self, lua: &RawLua) -> Result<(c_int, bool)> {
485 match self.status_inner(lua) {
486 ThreadStatusInner::New(nargs) => Ok((nargs, false)),
487 ThreadStatusInner::Yielded(nargs) => {
488 let hook_yielded = self.is_hook_yielded(lua);
489 Ok((if hook_yielded { 0 } else { nargs }, hook_yielded))
490 }
491 _ => Err(Error::CoroutineUnresumable),
492 }
493 }
494
495 fn is_hook_yielded(&self, lua: &RawLua) -> bool {
497 unsafe { lua.is_hook_yielded(self.state()) }
498 }
499
500 #[inline(always)]
503 pub fn is_resumable(&self) -> bool {
504 self.status() == ThreadStatus::Resumable
505 }
506
507 #[inline(always)]
509 pub fn is_running(&self) -> bool {
510 self.status() == ThreadStatus::Running
511 }
512
513 #[inline(always)]
518 pub fn is_normal(&self) -> bool {
519 self.status() == ThreadStatus::Normal
520 }
521
522 #[inline(always)]
524 pub fn is_finished(&self) -> bool {
525 self.status() == ThreadStatus::Finished
526 }
527
528 #[inline(always)]
530 pub fn is_error(&self) -> bool {
531 self.status() == ThreadStatus::Error
532 }
533
534 #[cfg(not(feature = "luau"))]
543 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
544 pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
545 where
546 F: Fn(&crate::Lua, &Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
547 {
548 let lua = self.0.lua.lock();
549 unsafe {
550 lua.set_thread_hook(
551 self.state(),
552 HookKind::Thread(triggers, crate::types::XRc::new(callback)),
553 )
554 }
555 }
556
557 #[cfg(not(feature = "luau"))]
559 #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
560 pub fn remove_hook(&self) {
561 let lua = self.0.lua.lock();
562 unsafe {
563 lua.remove_thread_hook(self.state());
564 }
565 }
566
567 pub fn reset(&self, func: Function) -> Result<()> {
582 let lua = self.0.lua.lock();
583 assert!(
584 lua.weak() == &func.0.lua,
585 "Lua instance passed Value created from a different main Lua state"
586 );
587 check_thread_reentrancy(self.state(), &lua)?;
588 let thread_state = self.state();
589 unsafe {
590 let status = self.status_inner(&lua);
591 self.reset_inner(status)?;
592
593 ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
595
596 #[cfg(feature = "luau")]
597 {
598 ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
600 ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
601 }
602
603 Ok(())
604 }
605 }
606
607 unsafe fn reset_inner(&self, status: ThreadStatusInner) -> Result<()> {
608 match status {
609 ThreadStatusInner::New(_) => {
610 ffi::lua_settop(self.state(), 0);
612 Ok(())
613 }
614 ThreadStatusInner::Running => Err(Error::runtime("cannot reset a running thread")),
615 ThreadStatusInner::Normal => Err(Error::runtime("cannot reset a normal thread")),
616 ThreadStatusInner::Finished => Ok(()),
617 #[cfg(not(any(feature = "lua55", feature = "lua54", feature = "luau")))]
618 ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
619 Err(Error::runtime("cannot reset non-finished thread"))
620 }
621 #[cfg(any(feature = "lua55", feature = "lua54", feature = "luau"))]
622 ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
623 let thread_state = self.state();
624
625 #[cfg(all(feature = "lua54", not(feature = "vendored")))]
626 let status = ffi::lua_resetthread(thread_state);
627 #[cfg(any(feature = "lua55", all(feature = "lua54", feature = "vendored")))]
628 let status = {
629 let lua = self.0.lua.lock();
630 ffi::lua_closethread(thread_state, lua.state())
631 };
632 #[cfg(any(feature = "lua55", feature = "lua54"))]
633 if status != ffi::LUA_OK {
634 return Err(pop_error(thread_state, status));
635 }
636 #[cfg(feature = "luau")]
637 ffi::lua_resetthread(thread_state);
638
639 Ok(())
640 }
641 }
642 }
643
644 #[cfg(feature = "async")]
691 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
692 pub fn into_async<R>(self, args: impl IntoLuaMulti) -> Result<AsyncThread<R>>
693 where
694 R: FromLuaMulti,
695 {
696 let lua = self.0.lua.lock();
697 check_thread_reentrancy(self.state(), &lua)?;
698 let (_, hook_yielded) = self.resumable_state(&lua)?;
699
700 let state = lua.state();
701 let thread_state = self.state();
702 unsafe {
703 let _sg = StackGuard::new(state);
704
705 if !hook_yielded {
706 let nargs = args.push_into_stack_multi(&lua)?;
707 if nargs > 0 {
708 check_stack(thread_state, nargs)?;
709 ffi::lua_xmove(state, thread_state, nargs);
710 }
711 }
712
713 Ok(AsyncThread {
714 thread: self,
715 ret: PhantomData,
716 recycle: false,
717 })
718 }
719 }
720
721 #[cfg(any(feature = "luau", doc))]
757 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
758 pub fn sandbox(&self) -> Result<()> {
759 let lua = self.0.lua.lock();
760 let state = lua.state();
761 let thread_state = self.state();
762 unsafe {
763 check_stack(thread_state, 3)?;
764 check_stack(state, 3)?;
765 protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
766 }
767 }
768
769 #[inline]
775 pub fn to_pointer(&self) -> *const c_void {
776 self.0.to_pointer()
777 }
778}
779
780impl fmt::Debug for Thread {
781 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
782 fmt.debug_tuple("Thread").field(&self.0).finish()
783 }
784}
785
786impl LuaType for Thread {
787 const TYPE_ID: c_int = ffi::LUA_TTHREAD;
788}
789
790#[cfg(feature = "async")]
791impl<R> AsyncThread<R> {
792 #[inline(always)]
793 pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
794 self.recycle = recyclable;
795 }
796
797 #[inline(always)]
798 pub(crate) fn thread(&self) -> &Thread {
799 &self.thread
800 }
801}
802
803#[cfg(feature = "async")]
804impl<R> Drop for AsyncThread<R> {
805 fn drop(&mut self) {
806 if self.recycle
807 && let Some(lua) = self.thread.0.lua.try_lock()
808 {
809 unsafe {
810 #[cfg(feature = "luau")]
811 if lua.is_running_gc() {
812 lua.update_thread_ownership(&self.thread, None);
813 return;
814 }
815
816 let mut status = self.thread.status_inner(&lua);
817 if matches!(status, ThreadStatusInner::Yielded(0)) && !self.thread.is_hook_yielded(&lua) {
818 ffi::lua_pushlightuserdata(self.thread.1, crate::Lua::poll_terminate().0);
820 if let Ok((new_status, _)) = self.thread.resume_inner(&lua, 1) {
821 status = new_status;
823 }
824 }
825
826 if self.thread.reset_inner(status).is_ok() {
828 lua.recycle_thread(&mut self.thread);
829 }
830 lua.update_thread_ownership(&self.thread, None);
831 }
832 }
833 }
834}
835
836#[cfg(feature = "async")]
837impl<R: FromLuaMulti> Stream for AsyncThread<R> {
838 type Item = Result<R>;
839
840 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
841 let lua = self.thread.0.lua.lock();
842 check_thread_reentrancy(self.thread.state(), &lua)?;
843 let mut nargs = match self.thread.resumable_state(&lua) {
844 Ok((nargs, _)) => nargs,
845 Err(_) => return Poll::Ready(None),
846 };
847
848 let state = lua.state();
849 let thread_state = self.thread.state();
850 unsafe {
851 let _sg = StackGuard::new(state);
852 let _wg = WakerGuard::new(&lua, cx.waker());
853
854 let on_resume = lua.thread_event_triggers().on_resume;
856 if exec_thread_event(&lua, on_resume, thread_state, || {
857 ThreadEvent::Resume(self.thread.clone())
858 })? {
859 nargs = match self.thread.resumable_state(&lua) {
860 Ok((nargs, _)) => nargs,
861 Err(_) => return Poll::Ready(None),
862 };
863 }
864
865 let mut thread_sg = StackGuard::with_top(thread_state, 0);
866 let (status, nresults) = (self.thread).resume_inner(&lua, nargs)?;
867 let hook_yielded = status.is_yielded() && self.thread.is_hook_yielded(&lua);
868 if hook_yielded {
869 debug_assert_eq!(nresults, 0);
870 thread_sg.keep(ffi::lua_gettop(thread_state));
871 }
872
873 if status.is_yielded() && !hook_yielded && nresults == 1 && is_poll_pending(thread_state) {
874 let on_yield = lua.thread_event_triggers().on_yield;
876 exec_thread_event(&lua, on_yield, thread_state, || {
877 ThreadEvent::Yield(self.thread.clone())
878 })?;
879 return Poll::Pending;
880 }
881
882 check_stack(state, nresults + 1)?;
883 ffi::lua_xmove(thread_state, state, nresults);
884
885 if status.is_yielded() {
886 let on_yield = lua.thread_event_triggers().on_yield;
887 exec_thread_event(&lua, on_yield, thread_state, || {
888 ThreadEvent::Yield(self.thread.clone())
889 })?;
890 cx.waker().wake_by_ref();
892 }
893
894 Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
895 }
896 }
897}
898
899#[cfg(feature = "async")]
900impl<R: FromLuaMulti> Future for AsyncThread<R> {
901 type Output = Result<R>;
902
903 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
904 let lua = self.thread.0.lua.lock();
905 check_thread_reentrancy(self.thread.state(), &lua)?;
906 let (mut nargs, _) = self.thread.resumable_state(&lua)?;
907
908 let state = lua.state();
909 let thread_state = self.thread.state();
910 unsafe {
911 let _sg = StackGuard::new(state);
912 let _wg = WakerGuard::new(&lua, cx.waker());
913
914 let on_resume = lua.thread_event_triggers().on_resume;
916 if exec_thread_event(&lua, on_resume, thread_state, || {
917 ThreadEvent::Resume(self.thread.clone())
918 })? {
919 (nargs, _) = self.thread.resumable_state(&lua)?;
920 }
921
922 let mut thread_sg = StackGuard::with_top(thread_state, 0);
923 let (status, nresults) = self.thread.resume_inner(&lua, nargs)?;
924 let hook_yielded = status.is_yielded() && self.thread.is_hook_yielded(&lua);
925 if hook_yielded {
926 debug_assert_eq!(nresults, 0);
927 thread_sg.keep(ffi::lua_gettop(thread_state));
928 }
929
930 if status.is_yielded() {
931 let pending = !hook_yielded && nresults == 1 && is_poll_pending(thread_state);
932
933 let on_yield = lua.thread_event_triggers().on_yield;
935 exec_thread_event(&lua, on_yield, thread_state, || {
936 ThreadEvent::Yield(self.thread.clone())
937 })?;
938
939 if !pending {
940 cx.waker().wake_by_ref();
942 }
943 return Poll::Pending;
944 }
945
946 check_stack(state, nresults + 1)?;
947 ffi::lua_xmove(thread_state, state, nresults);
948
949 Poll::Ready(R::from_stack_multi(nresults, &lua))
950 }
951 }
952}
953
954#[cfg(feature = "async")]
955#[inline(always)]
956unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
957 ffi::lua_tolightuserdata(state, -1) == crate::Lua::poll_pending().0
958}
959
960#[cfg(feature = "async")]
961struct WakerGuard<'lua, 'a> {
962 lua: &'lua RawLua,
963 prev: NonNull<Waker>,
964 _phantom: PhantomData<&'a ()>,
965}
966
967#[cfg(feature = "async")]
968impl<'lua, 'a> WakerGuard<'lua, 'a> {
969 #[inline]
970 pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
971 let prev = lua.set_waker(NonNull::from(waker));
972 Ok(WakerGuard {
973 lua,
974 prev,
975 _phantom: PhantomData,
976 })
977 }
978}
979
980#[cfg(feature = "async")]
981impl Drop for WakerGuard<'_, '_> {
982 fn drop(&mut self) {
983 self.lua.set_waker(self.prev);
984 }
985}
986
987#[cfg(test)]
988mod assertions {
989 use super::*;
990
991 #[cfg(not(feature = "send"))]
992 static_assertions::assert_not_impl_any!(Thread: Send);
993 #[cfg(feature = "send")]
994 static_assertions::assert_impl_all!(Thread: Send, Sync);
995 #[cfg(all(feature = "async", not(feature = "send")))]
996 static_assertions::assert_not_impl_any!(AsyncThread<()>: Send);
997 #[cfg(all(feature = "async", feature = "send"))]
998 static_assertions::assert_impl_all!(AsyncThread<()>: Send, Sync);
999}