@@ -696,56 +696,91 @@ impl Lua {
696
696
}
697
697
}
698
698
699
- /// Sets a thread event callback that will be called when a thread is created or destroyed.
699
+ /// Sets a thread creation callback that will be called when a thread is created.
700
+ #[ cfg( any( feature = "luau" , doc) ) ]
701
+ #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
702
+ pub fn set_thread_creation_callback < F > ( & self , callback : F )
703
+ where
704
+ F : Fn ( & Lua , Thread ) -> Result < ( ) > + MaybeSend + ' static ,
705
+ {
706
+ let lua = self . lock ( ) ;
707
+ unsafe {
708
+ ( * lua. extra . get ( ) ) . thread_creation_callback = Some ( XRc :: new ( callback) ) ;
709
+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( Self :: userthread_proc) ;
710
+ }
711
+ }
712
+
713
+ /// Sets a thread collection callback that will be called when a thread is destroyed.
700
714
///
701
- /// The callback is called with a [`Value`] argument that is either:
702
- /// - A [`Thread`] object when thread is created
703
- /// - A [`LightUserData`] when thread is destroyed
715
+ /// Luau GC does not support exceptions during collection, so the callback must be
716
+ /// non-panicking. If the callback panics, the program will be aborted.
704
717
#[ cfg( any( feature = "luau" , doc) ) ]
705
718
#[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
706
- pub fn set_thread_event_callback < F > ( & self , callback : F )
719
+ pub fn set_thread_collection_callback < F > ( & self , callback : F )
707
720
where
708
- F : Fn ( & Lua , Value ) -> Result < ( ) > + MaybeSend + ' static ,
721
+ F : Fn ( crate :: LightUserData ) + MaybeSend + ' static ,
709
722
{
710
- unsafe extern "C-unwind" fn userthread_proc ( parent : * mut ffi:: lua_State , child : * mut ffi:: lua_State ) {
711
- let extra = ExtraData :: get ( child) ;
712
- let thread_cb = match ( * extra) . userthread_callback {
723
+ let lua = self . lock ( ) ;
724
+ unsafe {
725
+ ( * lua. extra . get ( ) ) . thread_collection_callback = Some ( XRc :: new ( callback) ) ;
726
+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( Self :: userthread_proc) ;
727
+ }
728
+ }
729
+
730
+ #[ cfg( feature = "luau" ) ]
731
+ unsafe extern "C-unwind" fn userthread_proc ( parent : * mut ffi:: lua_State , child : * mut ffi:: lua_State ) {
732
+ let extra = ExtraData :: get ( child) ;
733
+ if !parent. is_null ( ) {
734
+ // Thread is created
735
+ let callback = match ( * extra) . thread_creation_callback {
713
736
Some ( ref cb) => cb. clone ( ) ,
714
737
None => return ,
715
738
} ;
716
- if XRc :: strong_count ( & thread_cb ) > 2 {
739
+ if XRc :: strong_count ( & callback ) > 2 {
717
740
return ; // Don't allow recursion
718
741
}
719
- let value = match parent. is_null ( ) {
720
- // Thread is about to be destroyed, pass light userdata
721
- true => Value :: LightUserData ( crate :: LightUserData ( child as _ ) ) ,
722
- false => {
723
- // Thread is created, pass thread object
724
- ffi:: lua_pushthread ( child) ;
725
- ffi:: lua_xmove ( child, ( * extra) . ref_thread , 1 ) ;
726
- Value :: Thread ( Thread ( ( * extra) . raw_lua ( ) . pop_ref_thread ( ) , child) )
727
- }
728
- } ;
742
+ ffi:: lua_pushthread ( child) ;
743
+ ffi:: lua_xmove ( child, ( * extra) . ref_thread , 1 ) ;
744
+ let value = Thread ( ( * extra) . raw_lua ( ) . pop_ref_thread ( ) , child) ;
745
+ let _guard = StateGuard :: new ( ( * extra) . raw_lua ( ) , parent) ;
729
746
callback_error_ext ( ( * extra) . raw_lua ( ) . state ( ) , extra, false , move |extra, _| {
730
- thread_cb ( ( * extra) . lua ( ) , value)
747
+ callback ( ( * extra) . lua ( ) , value)
731
748
} )
732
- }
749
+ } else {
750
+ // Thread is about to be collected
751
+ let callback = match ( * extra) . thread_collection_callback {
752
+ Some ( ref cb) => cb. clone ( ) ,
753
+ None => return ,
754
+ } ;
733
755
734
- // Set thread callback
735
- let lua = self . lock ( ) ;
736
- unsafe {
737
- ( * lua. extra . get ( ) ) . userthread_callback = Some ( XRc :: new ( callback) ) ;
738
- ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( userthread_proc) ;
756
+ // We need to wrap the callback call in non-unwind function as it's not safe to unwind when
757
+ // Luau GC is running.
758
+ // This will trigger `abort()` if the callback panics.
759
+ unsafe extern "C" fn run_callback (
760
+ callback : * const crate :: types:: ThreadCollectionCallback ,
761
+ value : * mut ffi:: lua_State ,
762
+ ) {
763
+ ( * callback) ( crate :: LightUserData ( value as _ ) ) ;
764
+ }
765
+
766
+ ( * extra) . running_gc = true ;
767
+ run_callback ( & callback, child) ;
768
+ ( * extra) . running_gc = false ;
739
769
}
740
770
}
741
771
742
- /// Removes any thread event callback previously set by `set_thread_event_callback`.
772
+ /// Removes any thread creation or collection callbacks previously set by
773
+ /// [`Lua::set_thread_creation_callback`] or [`Lua::set_thread_collection_callback`].
774
+ ///
775
+ /// This function has no effect if a thread callbacks were not previously set.
743
776
#[ cfg( any( feature = "luau" , doc) ) ]
744
777
#[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
745
- pub fn remove_thread_event_callback ( & self ) {
778
+ pub fn remove_thread_callbacks ( & self ) {
746
779
let lua = self . lock ( ) ;
747
780
unsafe {
748
- ( * lua. extra . get ( ) ) . userthread_callback = None ;
781
+ let extra = lua. extra . get ( ) ;
782
+ ( * extra) . thread_creation_callback = None ;
783
+ ( * extra) . thread_collection_callback = None ;
749
784
( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = None ;
750
785
}
751
786
}
@@ -2039,8 +2074,8 @@ impl Lua {
2039
2074
pub ( crate ) fn lock ( & self ) -> ReentrantMutexGuard < RawLua > {
2040
2075
let rawlua = self . raw . lock ( ) ;
2041
2076
#[ cfg( feature = "luau" ) ]
2042
- if unsafe { ( * rawlua. extra . get ( ) ) . running_userdata_gc } {
2043
- panic ! ( "Luau VM is suspended while userdata destructor is running" ) ;
2077
+ if unsafe { ( * rawlua. extra . get ( ) ) . running_gc } {
2078
+ panic ! ( "Luau VM is suspended while GC is running" ) ;
2044
2079
}
2045
2080
rawlua
2046
2081
}
@@ -2066,8 +2101,8 @@ impl WeakLua {
2066
2101
pub ( crate ) fn lock ( & self ) -> LuaGuard {
2067
2102
let guard = LuaGuard :: new ( self . 0 . upgrade ( ) . expect ( "Lua instance is destroyed" ) ) ;
2068
2103
#[ cfg( feature = "luau" ) ]
2069
- if unsafe { ( * guard. extra . get ( ) ) . running_userdata_gc } {
2070
- panic ! ( "Luau VM is suspended while userdata destructor is running" ) ;
2104
+ if unsafe { ( * guard. extra . get ( ) ) . running_gc } {
2105
+ panic ! ( "Luau VM is suspended while GC is running" ) ;
2071
2106
}
2072
2107
guard
2073
2108
}
0 commit comments