@@ -22,7 +22,7 @@ use crate::scope::Scope;
2222use crate :: stdlib:: StdLib ;
2323use crate :: string:: LuaString ;
2424use crate :: table:: Table ;
25- use crate :: thread:: Thread ;
25+ use crate :: thread:: { Thread , ThreadEvent , ThreadTriggers } ;
2626use crate :: traits:: { FromLua , FromLuaMulti , IntoLua , IntoLuaMulti } ;
2727use crate :: types:: {
2828 AppDataRef , AppDataRefMut , ArcReentrantMutexGuard , Integer , LuaType , MaybeSend , MaybeSync , Number ,
@@ -842,92 +842,87 @@ impl Lua {
842842 }
843843 }
844844
845- /// Sets a thread creation callback that will be called when a thread is created.
846- #[ cfg( any( feature = "luau" , doc) ) ]
847- #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
848- pub fn set_thread_creation_callback < F > ( & self , callback : F )
845+ /// Sets a callback invoked when thread lifecycle events occur.
846+ ///
847+ /// `triggers` controls which events trigger the callback, see [`ThreadTriggers`] for more
848+ /// details.
849+ ///
850+ /// Only one callback can be registered at a time. Calling this again replaces the previous
851+ /// callback and its triggers.
852+ ///
853+ /// # Example
854+ ///
855+ /// Subscribe only to yield events:
856+ ///
857+ /// ```
858+ /// # use mlua::{Lua, Result, ThreadTriggers, ThreadEvent};
859+ /// # fn main() -> Result<()> {
860+ /// let lua = Lua::new();
861+ /// lua.set_thread_event_callback(
862+ /// ThreadTriggers::ON_YIELD,
863+ /// |_lua, event| {
864+ /// if let ThreadEvent::Yield(thread) = event {
865+ /// println!("thread yielded");
866+ /// }
867+ /// Ok(())
868+ /// },
869+ /// );
870+ /// # Ok(())
871+ /// # }
872+ /// ```
873+ pub fn set_thread_event_callback < F > ( & self , triggers : ThreadTriggers , callback : F )
849874 where
850- F : Fn ( & Lua , Thread ) -> Result < ( ) > + MaybeSend + ' static ,
875+ F : Fn ( & Lua , ThreadEvent ) -> Result < ( ) > + MaybeSend + ' static ,
851876 {
852877 let lua = self . lock ( ) ;
853878 unsafe {
854- ( * lua. extra . get ( ) ) . thread_creation_callback = Some ( XRc :: new ( callback) ) ;
855- ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( Self :: userthread_proc) ;
879+ ( * lua. extra . get ( ) ) . thread_triggers = triggers;
880+ ( * lua. extra . get ( ) ) . thread_event_callback = Some ( XRc :: new ( callback) ) ;
881+ #[ cfg( feature = "luau" ) ]
882+ {
883+ let proc = Self :: userthread_proc as _ ;
884+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = triggers. on_create . then ( || proc) ;
885+ }
856886 }
857887 }
858888
859- /// Sets a thread collection callback that will be called when a thread is destroyed .
889+ /// Removes the thread event callback previously set by [`Lua::set_thread_event_callback`] .
860890 ///
861- /// Luau GC does not support exceptions during collection, so the callback must be
862- /// non-panicking. If the callback panics, the program will be aborted.
863- #[ cfg( any( feature = "luau" , doc) ) ]
864- #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
865- pub fn set_thread_collection_callback < F > ( & self , callback : F )
866- where
867- F : Fn ( crate :: LightUserData ) + MaybeSend + ' static ,
868- {
891+ /// This function has no effect if a callback was not previously set.
892+ pub fn remove_thread_event_callback ( & self ) {
869893 let lua = self . lock ( ) ;
894+ let extra = lua. extra . get ( ) ;
870895 unsafe {
871- ( * lua. extra . get ( ) ) . thread_collection_callback = Some ( XRc :: new ( callback) ) ;
872- ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( Self :: userthread_proc) ;
896+ ( * extra) . thread_triggers = ThreadTriggers :: new ( ) ;
897+ ( * extra) . thread_event_callback = None ;
898+ #[ cfg( feature = "luau" ) ]
899+ {
900+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = None ;
901+ }
873902 }
874903 }
875904
876905 #[ cfg( feature = "luau" ) ]
877906 unsafe extern "C-unwind" fn userthread_proc ( parent : * mut ffi:: lua_State , child : * mut ffi:: lua_State ) {
878- let extra = ExtraData :: get ( child) ;
879- if !parent. is_null ( ) {
880- // Thread is created
881- let callback = match ( * extra) . thread_creation_callback {
882- Some ( ref cb) => cb. clone ( ) ,
883- None => return ,
884- } ;
885- if XRc :: strong_count ( & callback) > 2 {
886- return ; // Don't allow recursion
887- }
888- ffi:: lua_pushthread ( child) ;
889- ffi:: lua_xmove ( child, ( * extra) . ref_thread , 1 ) ;
890- let value = Thread ( ( * extra) . raw_lua ( ) . pop_ref_thread ( ) , child) ;
891- callback_error_ext ( parent, extra, false , move |extra, _| {
892- callback ( ( * extra) . lua ( ) , value)
893- } )
894- } else {
895- // Thread is about to be collected
896- let callback = match ( * extra) . thread_collection_callback {
897- Some ( ref cb) => cb. clone ( ) ,
898- None => return ,
899- } ;
900-
901- // We need to wrap the callback call in non-unwind function as it's not safe to unwind when
902- // Luau GC is running.
903- // This will trigger `abort()` if the callback panics.
904- unsafe extern "C" fn run_callback (
905- callback : * const crate :: types:: ThreadCollectionCallback ,
906- value : * mut ffi:: lua_State ,
907- ) {
908- ( * callback) ( crate :: LightUserData ( value as _ ) ) ;
909- }
910-
911- ( * extra) . running_gc = true ;
912- run_callback ( & callback, child) ;
913- ( * extra) . running_gc = false ;
907+ // Only handle thread creation
908+ if parent. is_null ( ) {
909+ return ;
914910 }
915- }
916911
917- /// Removes any thread creation or collection callbacks previously set by
918- /// [`Lua::set_thread_creation_callback`] or [`Lua::set_thread_collection_callback`].
919- ///
920- /// This function has no effect if a thread callbacks were not previously set.
921- #[ cfg( any( feature = "luau" , doc) ) ]
922- #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
923- pub fn remove_thread_callbacks ( & self ) {
924- let lua = self . lock ( ) ;
925- unsafe {
926- let extra = lua. extra . get ( ) ;
927- ( * extra) . thread_creation_callback = None ;
928- ( * extra) . thread_collection_callback = None ;
929- ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = None ;
912+ let extra = ExtraData :: get ( child) ;
913+ if !( * extra) . thread_triggers . on_create {
914+ return ;
930915 }
916+ let callback = match & ( * extra) . thread_event_callback {
917+ Some ( cb) if XRc :: strong_count ( cb) == 1 => cb. clone ( ) ,
918+ _ => return ,
919+ } ;
920+ ffi:: lua_pushthread ( child) ;
921+ ffi:: lua_xmove ( child, ( * extra) . ref_thread , 1 ) ;
922+ let thread = Thread ( ( * extra) . raw_lua ( ) . pop_ref_thread ( ) , child) ;
923+ callback_error_ext ( parent, extra, false , move |extra, _| {
924+ callback ( ( * extra) . lua ( ) , ThreadEvent :: Create ( thread) )
925+ } )
931926 }
932927
933928 /// Sets the warning function to be used by Lua to emit warnings.
0 commit comments