@@ -23,6 +23,10 @@ use crate::types::{
2323 AppDataRef , AppDataRefMut , ArcReentrantMutexGuard , Integer , LuaType , MaybeSend , Number , ReentrantMutex ,
2424 ReentrantMutexGuard , RegistryKey , VmState , XRc , XWeak ,
2525} ;
26+
27+ #[ cfg( any( feature = "luau" , doc) ) ]
28+ #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
29+ use crate :: types:: ThreadEventInfo ;
2630use crate :: userdata:: { AnyUserData , UserData , UserDataProxy , UserDataRegistry , UserDataStorage } ;
2731use crate :: util:: {
2832 assert_stack, check_stack, protect_lua_closure, push_string, push_table, rawset_field, StackGuard ,
@@ -671,6 +675,72 @@ impl Lua {
671675 }
672676 }
673677
678+ /// Sets a callback that will be called by Luau whenever a thread is created/destroyed.
679+ ///
680+ /// Often used for keeping track of threads.
681+ #[ cfg( any( feature = "luau" , doc) ) ]
682+ #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
683+ pub fn set_thread_event_callback < F > ( & self , callback : F )
684+ where
685+ F : Fn ( & Lua , ThreadEventInfo ) -> Result < ( ) > + MaybeSend + ' static ,
686+ {
687+ use std:: rc:: Rc ;
688+
689+ unsafe extern "C-unwind" fn userthread_proc ( parent : * mut ffi:: lua_State , state : * mut ffi:: lua_State ) {
690+ callback_error_ext ( state, ptr:: null_mut ( ) , move |extra, _| {
691+ let raw_lua: & RawLua = ( * extra) . raw_lua ( ) ;
692+ let _guard = StateGuard :: new ( raw_lua, state) ;
693+
694+ let userthread_cb = ( * extra) . userthread_callback . clone ( ) ;
695+ let userthread_cb =
696+ mlua_expect ! ( userthread_cb, "no userthread callback set in userthread_proc" ) ;
697+ if parent. is_null ( ) {
698+ raw_lua. push ( Value :: Nil ) . unwrap ( ) ;
699+ } else {
700+ raw_lua. push_ref_thread ( parent) . unwrap ( ) ;
701+ }
702+ if parent. is_null ( ) {
703+ let event_info = ThreadEventInfo :: Destroyed ( state. cast_const ( ) . cast ( ) ) ;
704+ let main_state = raw_lua. main_state ( ) ;
705+ if main_state == state {
706+ return Ok ( ( ) ) ; // Don't process Destroyed event on main thread.
707+ }
708+ let main_extra = ExtraData :: get ( main_state) ;
709+ let main_raw_lua: & RawLua = ( * main_extra) . raw_lua ( ) ;
710+ let _guard = StateGuard :: new ( main_raw_lua, state) ;
711+ userthread_cb ( ( * main_extra) . lua ( ) , event_info)
712+ } else {
713+ raw_lua. push_ref_thread ( parent) . unwrap ( ) ;
714+ let event_info = match raw_lua. pop_value ( ) {
715+ Value :: Thread ( thr) => ThreadEventInfo :: Created ( thr) ,
716+ _ => unimplemented ! ( ) ,
717+ } ;
718+ userthread_cb ( ( * extra) . lua ( ) , event_info)
719+ }
720+ } ) ;
721+ }
722+
723+ // Set interrupt callback
724+ let lua = self . lock ( ) ;
725+ unsafe {
726+ ( * lua. extra . get ( ) ) . userthread_callback = Some ( Rc :: new ( callback) ) ;
727+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = Some ( userthread_proc) ;
728+ }
729+ }
730+
731+ /// Removes any thread event function previously set by `set_thread_event_callback`.
732+ ///
733+ /// This function has no effect if a callback was not previously set.
734+ #[ cfg( any( feature = "luau" , doc) ) ]
735+ #[ cfg_attr( docsrs, doc( cfg( feature = "luau" ) ) ) ]
736+ pub fn remove_thread_event_callback ( & self ) {
737+ let lua = self . lock ( ) ;
738+ unsafe {
739+ ( * lua. extra . get ( ) ) . userthread_callback = None ;
740+ ( * ffi:: lua_callbacks ( lua. main_state ( ) ) ) . userthread = None ;
741+ }
742+ }
743+
674744 /// Sets the warning function to be used by Lua to emit warnings.
675745 ///
676746 /// Requires `feature = "lua54"`
0 commit comments