@@ -185,12 +185,6 @@ enum ThreadStatusInner {
185185}
186186
187187impl ThreadStatusInner {
188- #[ cfg( feature = "async" ) ]
189- #[ inline( always) ]
190- fn is_resumable ( self ) -> bool {
191- matches ! ( self , ThreadStatusInner :: New ( _) | ThreadStatusInner :: Yielded ( _) )
192- }
193-
194188 #[ inline( always) ]
195189 fn is_yielded ( self ) -> bool {
196190 matches ! ( self , ThreadStatusInner :: Yielded ( _) )
@@ -329,7 +323,7 @@ impl Thread {
329323 {
330324 let lua = self . 0 . lua . lock ( ) ;
331325 check_thread_reentrancy ( self . state ( ) , & lua) ?;
332- let mut pushed_nargs = self . resumable_nargs ( & lua) ?;
326+ let ( mut pushed_nargs, mut hook_yielded ) = self . resumable_state ( & lua) ?;
333327
334328 let state = lua. state ( ) ;
335329 let thread_state = self . state ( ) ;
@@ -341,18 +335,24 @@ impl Thread {
341335 if exec_thread_event ( & lua, on_resume, thread_state, || {
342336 ThreadEvent :: Resume ( self . clone ( ) )
343337 } ) ? {
344- pushed_nargs = self . resumable_nargs ( & lua) ?;
338+ ( pushed_nargs, hook_yielded ) = self . resumable_state ( & lua) ?;
345339 }
346340
347- let nargs = args. push_into_stack_multi ( & lua) ?;
348- if nargs > 0 {
349- check_stack ( thread_state, nargs) ?;
350- ffi:: lua_xmove ( state, thread_state, nargs) ;
351- pushed_nargs += nargs;
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+ }
352348 }
353349
354- let _thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
350+ let mut thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
355351 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+ }
356356
357357 check_stack ( state, nresults + 1 ) ?;
358358 ffi:: lua_xmove ( thread_state, state, nresults) ;
@@ -478,15 +478,24 @@ impl Thread {
478478 }
479479 }
480480
481- /// Returns the number of pending arguments on the thread stack if the thread is resumable .
481+ /// Returns the pending argument count and whether the thread was interrupted by a hook .
482482 #[ inline]
483- fn resumable_nargs ( & self , lua : & RawLua ) -> Result < c_int > {
483+ fn resumable_state ( & self , lua : & RawLua ) -> Result < ( c_int , bool ) > {
484484 match self . status_inner ( lua) {
485- ThreadStatusInner :: New ( nargs) | ThreadStatusInner :: Yielded ( nargs) => Ok ( nargs) ,
485+ ThreadStatusInner :: New ( nargs) => Ok ( ( nargs, false ) ) ,
486+ ThreadStatusInner :: Yielded ( nargs) => {
487+ let hook_yielded = self . is_hook_yielded ( lua) ;
488+ Ok ( ( if hook_yielded { 0 } else { nargs } , hook_yielded) )
489+ }
486490 _ => Err ( Error :: CoroutineUnresumable ) ,
487491 }
488492 }
489493
494+ /// Distinguishes a hook interruption from a normal yield.
495+ fn is_hook_yielded ( & self , lua : & RawLua ) -> bool {
496+ unsafe { lua. is_hook_yielded ( self . state ( ) ) }
497+ }
498+
490499 /// Returns `true` if this thread is resumable (meaning it can be resumed by calling
491500 /// [`Thread::resume`]).
492501 #[ inline( always) ]
@@ -548,9 +557,9 @@ impl Thread {
548557 #[ cfg( not( feature = "luau" ) ) ]
549558 #[ cfg_attr( docsrs, doc( cfg( not( feature = "luau" ) ) ) ) ]
550559 pub fn remove_hook ( & self ) {
551- let _lua = self . 0 . lua . lock ( ) ;
560+ let lua = self . 0 . lua . lock ( ) ;
552561 unsafe {
553- ffi :: lua_sethook ( self . state ( ) , None , 0 , 0 ) ;
562+ lua . remove_thread_hook ( self . state ( ) ) ;
554563 }
555564 }
556565
@@ -681,19 +690,19 @@ impl Thread {
681690 {
682691 let lua = self . 0 . lua . lock ( ) ;
683692 check_thread_reentrancy ( self . state ( ) , & lua) ?;
684- if !self . status_inner ( & lua) . is_resumable ( ) {
685- return Err ( Error :: CoroutineUnresumable ) ;
686- }
693+ let ( _, hook_yielded) = self . resumable_state ( & lua) ?;
687694
688695 let state = lua. state ( ) ;
689696 let thread_state = self . state ( ) ;
690697 unsafe {
691698 let _sg = StackGuard :: new ( state) ;
692699
693- let nargs = args. push_into_stack_multi ( & lua) ?;
694- if nargs > 0 {
695- check_stack ( thread_state, nargs) ?;
696- ffi:: lua_xmove ( state, thread_state, nargs) ;
700+ if !hook_yielded {
701+ let nargs = args. push_into_stack_multi ( & lua) ?;
702+ if nargs > 0 {
703+ check_stack ( thread_state, nargs) ?;
704+ ffi:: lua_xmove ( state, thread_state, nargs) ;
705+ }
697706 }
698707
699708 Ok ( AsyncThread {
@@ -794,7 +803,7 @@ impl<R> Drop for AsyncThread<R> {
794803 {
795804 unsafe {
796805 let mut status = self . thread . status_inner ( & lua) ;
797- if matches ! ( status, ThreadStatusInner :: Yielded ( 0 ) ) {
806+ if matches ! ( status, ThreadStatusInner :: Yielded ( 0 ) ) && ! self . thread . is_hook_yielded ( & lua ) {
798807 // The thread is dropped while yielded, resume it with the "terminate" signal
799808 ffi:: lua_pushlightuserdata ( self . thread . 1 , crate :: Lua :: poll_terminate ( ) . 0 ) ;
800809 if let Ok ( ( new_status, _) ) = self . thread . resume_inner ( & lua, 1 ) {
@@ -820,32 +829,37 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
820829 fn poll_next ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
821830 let lua = self . thread . 0 . lua . lock ( ) ;
822831 check_thread_reentrancy ( self . thread . state ( ) , & lua) ?;
823- let mut nargs = match self . thread . resumable_nargs ( & lua) {
824- Ok ( nargs) => nargs,
832+ let mut nargs = match self . thread . resumable_state ( & lua) {
833+ Ok ( ( nargs, _ ) ) => nargs,
825834 Err ( _) => return Poll :: Ready ( None ) ,
826835 } ;
827836
828837 let state = lua. state ( ) ;
829838 let thread_state = self . thread . state ( ) ;
830839 unsafe {
831840 let _sg = StackGuard :: new ( state) ;
832- let _thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
841+ let mut thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
833842 let _wg = WakerGuard :: new ( & lua, cx. waker ( ) ) ;
834843
835844 // If the resume callback runs, it may touch this thread, so re-read the argument count
836845 let on_resume = lua. thread_event_triggers ( ) . on_resume ;
837846 if exec_thread_event ( & lua, on_resume, thread_state, || {
838847 ThreadEvent :: Resume ( self . thread . clone ( ) )
839848 } ) ? {
840- nargs = match self . thread . resumable_nargs ( & lua) {
841- Ok ( nargs) => nargs,
849+ nargs = match self . thread . resumable_state ( & lua) {
850+ Ok ( ( nargs, _ ) ) => nargs,
842851 Err ( _) => return Poll :: Ready ( None ) ,
843852 } ;
844853 }
845854
846855 let ( status, nresults) = ( self . thread ) . resume_inner ( & lua, nargs) ?;
856+ let hook_yielded = status. is_yielded ( ) && self . thread . is_hook_yielded ( & lua) ;
857+ if hook_yielded {
858+ debug_assert_eq ! ( nresults, 0 ) ;
859+ thread_sg. keep ( ffi:: lua_gettop ( thread_state) ) ;
860+ }
847861
848- if status. is_yielded ( ) && nresults == 1 && is_poll_pending ( thread_state) {
862+ if status. is_yielded ( ) && !hook_yielded && nresults == 1 && is_poll_pending ( thread_state) {
849863 // Exec thread yield callback
850864 let on_yield = lua. thread_event_triggers ( ) . on_yield ;
851865 exec_thread_event ( & lua, on_yield, thread_state, || {
@@ -878,27 +892,32 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
878892 fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
879893 let lua = self . thread . 0 . lua . lock ( ) ;
880894 check_thread_reentrancy ( self . thread . state ( ) , & lua) ?;
881- let mut nargs = self . thread . resumable_nargs ( & lua) ?;
895+ let ( mut nargs, _ ) = self . thread . resumable_state ( & lua) ?;
882896
883897 let state = lua. state ( ) ;
884898 let thread_state = self . thread . state ( ) ;
885899 unsafe {
886900 let _sg = StackGuard :: new ( state) ;
887- let _thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
901+ let mut thread_sg = StackGuard :: with_top ( thread_state, 0 ) ;
888902 let _wg = WakerGuard :: new ( & lua, cx. waker ( ) ) ;
889903
890904 // If the resume callback runs, it may touch this thread, so re-read the argument count
891905 let on_resume = lua. thread_event_triggers ( ) . on_resume ;
892906 if exec_thread_event ( & lua, on_resume, thread_state, || {
893907 ThreadEvent :: Resume ( self . thread . clone ( ) )
894908 } ) ? {
895- nargs = self . thread . resumable_nargs ( & lua) ?;
909+ ( nargs, _ ) = self . thread . resumable_state ( & lua) ?;
896910 }
897911
898912 let ( status, nresults) = self . thread . resume_inner ( & lua, nargs) ?;
913+ let hook_yielded = status. is_yielded ( ) && self . thread . is_hook_yielded ( & lua) ;
914+ if hook_yielded {
915+ debug_assert_eq ! ( nresults, 0 ) ;
916+ thread_sg. keep ( ffi:: lua_gettop ( thread_state) ) ;
917+ }
899918
900919 if status. is_yielded ( ) {
901- let pending = nresults == 1 && is_poll_pending ( thread_state) ;
920+ let pending = !hook_yielded && nresults == 1 && is_poll_pending ( thread_state) ;
902921
903922 // Exec thread yield callback
904923 let on_yield = lua. thread_event_triggers ( ) . on_yield ;
0 commit comments