1
- use std:: { ptr, str} ;
1
+ use std:: { mem , process , ptr, str} ;
2
2
use std:: sync:: { Arc , Mutex } ;
3
3
use std:: ops:: DerefMut ;
4
4
use std:: cell:: RefCell ;
5
5
use std:: ffi:: CString ;
6
- use std:: any:: TypeId ;
6
+ use std:: any:: { Any , TypeId } ;
7
7
use std:: marker:: PhantomData ;
8
8
use std:: collections:: HashMap ;
9
9
use std:: os:: raw:: { c_char, c_int, c_void} ;
10
- use std:: mem;
11
- use std:: process;
12
10
13
11
use libc;
14
12
@@ -30,12 +28,21 @@ pub struct Lua {
30
28
ephemeral : bool ,
31
29
}
32
30
31
+ /// Constructed by the `Lua::scope` method, allows temporarily passing to Lua userdata that is
32
+ /// !Send, and callbacks that are !Send and not 'static.
33
+ pub struct Scope < ' lua > {
34
+ lua : & ' lua Lua ,
35
+ destructors : RefCell < Vec < Box < FnMut ( * mut ffi:: lua_State ) -> Box < Any > > > > ,
36
+ }
37
+
33
38
// Data associated with the main lua_State via lua_getextraspace.
34
39
struct ExtraData {
35
40
registered_userdata : HashMap < TypeId , c_int > ,
36
- registry_drop_list : Arc < Mutex < Vec < c_int > > > ,
41
+ registry_unref_list : Arc < Mutex < Option < Vec < c_int > > > > ,
37
42
}
38
43
44
+ unsafe impl Send for Lua { }
45
+
39
46
impl Drop for Lua {
40
47
fn drop ( & mut self ) {
41
48
unsafe {
@@ -49,6 +56,7 @@ impl Drop for Lua {
49
56
}
50
57
51
58
let extra_data = * ( ffi:: lua_getextraspace ( self . state ) as * mut * mut ExtraData ) ;
59
+ * ( * extra_data) . registry_unref_list . lock ( ) . unwrap ( ) = None ;
52
60
Box :: from_raw ( extra_data) ;
53
61
54
62
ffi:: lua_close ( self . state ) ;
@@ -259,7 +267,7 @@ impl Lua {
259
267
where
260
268
A : FromLuaMulti < ' lua > ,
261
269
R : ToLuaMulti < ' lua > ,
262
- F : ' static + FnMut ( & ' lua Lua , A ) -> Result < R > ,
270
+ F : ' static + Send + FnMut ( & ' lua Lua , A ) -> Result < R > ,
263
271
{
264
272
self . create_callback_function ( Box :: new ( move |lua, args| {
265
273
func ( lua, A :: from_lua_multi ( args, lua) ?) ?. to_lua_multi ( lua)
@@ -286,25 +294,9 @@ impl Lua {
286
294
/// Create a Lua userdata object from a custom userdata type.
287
295
pub fn create_userdata < T > ( & self , data : T ) -> Result < AnyUserData >
288
296
where
289
- T : UserData ,
297
+ T : Send + UserData ,
290
298
{
291
- unsafe {
292
- stack_err_guard ( self . state , 0 , move || {
293
- check_stack ( self . state , 3 ) ;
294
-
295
- push_userdata :: < RefCell < T > > ( self . state , RefCell :: new ( data) ) ?;
296
-
297
- ffi:: lua_rawgeti (
298
- self . state ,
299
- ffi:: LUA_REGISTRYINDEX ,
300
- self . userdata_metatable :: < T > ( ) ? as ffi:: lua_Integer ,
301
- ) ;
302
-
303
- ffi:: lua_setmetatable ( self . state , -2 ) ;
304
-
305
- Ok ( AnyUserData ( self . pop_ref ( self . state ) ) )
306
- } )
307
- }
299
+ self . do_create_userdata ( data)
308
300
}
309
301
310
302
/// Returns a handle to the global environment.
@@ -318,6 +310,28 @@ impl Lua {
318
310
}
319
311
}
320
312
313
+ /// Calls the given function with a `Scope` parameter, giving the function the ability to create
314
+ /// userdata from rust types that are !Send, and rust callbacks that are !Send and not 'static.
315
+ /// The lifetime of any function or userdata created through `Scope` lasts only until the
316
+ /// completion of this method call, on completion all such created values are automatically
317
+ /// dropped and Lua references to them are invalidated. If a script accesses a value created
318
+ /// through `Scope` outside of this method, a Lua error will result. Since we can ensure the
319
+ /// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another
320
+ /// thread while `Scope` is live, it is safe to allow !Send datatypes and functions whose
321
+ /// lifetimes only outlive the scope lifetime.
322
+ pub fn scope < ' lua , F , R > ( & ' lua self , f : F ) -> R
323
+ where
324
+ F : FnOnce ( & mut Scope < ' lua > ) -> R ,
325
+ {
326
+ let mut scope = Scope {
327
+ lua : self ,
328
+ destructors : RefCell :: new ( Vec :: new ( ) ) ,
329
+ } ;
330
+ let r = f ( & mut scope) ;
331
+ drop ( scope) ;
332
+ r
333
+ }
334
+
321
335
/// Coerces a Lua value to a string.
322
336
///
323
337
/// The value must be a string (in which case this is a no-op) or a number.
@@ -492,7 +506,8 @@ impl Lua {
492
506
493
507
Ok ( RegistryKey {
494
508
registry_id,
495
- drop_list : ( * self . extra ( ) ) . registry_drop_list . clone ( ) ,
509
+ unref_list : ( * self . extra ( ) ) . registry_unref_list . clone ( ) ,
510
+ drop_unref : true ,
496
511
} )
497
512
} )
498
513
}
@@ -504,7 +519,7 @@ impl Lua {
504
519
/// value previously placed by `create_registry_value`.
505
520
pub fn registry_value < ' lua , T : FromLua < ' lua > > ( & ' lua self , key : & RegistryKey ) -> Result < T > {
506
521
unsafe {
507
- if !Arc :: ptr_eq ( & key. drop_list , & ( * self . extra ( ) ) . registry_drop_list ) {
522
+ if !Arc :: ptr_eq ( & key. unref_list , & ( * self . extra ( ) ) . registry_unref_list ) {
508
523
return Err ( Error :: MismatchedRegistryKey ) ;
509
524
}
510
525
@@ -528,13 +543,12 @@ impl Lua {
528
543
/// `RegistryKey`s have been dropped.
529
544
pub fn remove_registry_value ( & self , mut key : RegistryKey ) -> Result < ( ) > {
530
545
unsafe {
531
- if !Arc :: ptr_eq ( & key. drop_list , & ( * self . extra ( ) ) . registry_drop_list ) {
546
+ if !Arc :: ptr_eq ( & key. unref_list , & ( * self . extra ( ) ) . registry_unref_list ) {
532
547
return Err ( Error :: MismatchedRegistryKey ) ;
533
548
}
534
549
535
550
ffi:: luaL_unref ( self . state , ffi:: LUA_REGISTRYINDEX , key. registry_id ) ;
536
- // Don't adding to the registry drop list when dropping the key
537
- key. registry_id = ffi:: LUA_REFNIL ;
551
+ key. drop_unref = false ;
538
552
Ok ( ( ) )
539
553
}
540
554
}
@@ -546,19 +560,19 @@ impl Lua {
546
560
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
547
561
/// matching `Lua` state.
548
562
pub fn owns_registry_value ( & self , key : & RegistryKey ) -> bool {
549
- unsafe { Arc :: ptr_eq ( & key. drop_list , & ( * self . extra ( ) ) . registry_drop_list ) }
563
+ unsafe { Arc :: ptr_eq ( & key. unref_list , & ( * self . extra ( ) ) . registry_unref_list ) }
550
564
}
551
565
552
566
/// Remove any registry values whose `RegistryKey`s have all been dropped. Unlike normal handle
553
567
/// values, `RegistryKey`s cannot automatically clean up their registry entries on Drop, but you
554
568
/// can call this method to remove any unreachable registry values.
555
569
pub fn expire_registry_values ( & self ) {
556
570
unsafe {
557
- let drop_list = mem:: replace (
558
- ( * self . extra ( ) ) . registry_drop_list . lock ( ) . unwrap ( ) . as_mut ( ) ,
559
- Vec :: new ( ) ,
571
+ let unref_list = mem:: replace (
572
+ & mut * ( * self . extra ( ) ) . registry_unref_list . lock ( ) . unwrap ( ) ,
573
+ Some ( Vec :: new ( ) ) ,
560
574
) ;
561
- for id in drop_list {
575
+ for id in unref_list . unwrap ( ) {
562
576
ffi:: luaL_unref ( self . state , ffi:: LUA_REGISTRYINDEX , id) ;
563
577
}
564
578
}
@@ -694,6 +708,7 @@ impl Lua {
694
708
LuaRef {
695
709
lua : self ,
696
710
registry_id : registry_id,
711
+ drop_unref : true ,
697
712
}
698
713
}
699
714
@@ -920,7 +935,7 @@ impl Lua {
920
935
921
936
let extra_data = Box :: into_raw ( Box :: new ( ExtraData {
922
937
registered_userdata : HashMap :: new ( ) ,
923
- registry_drop_list : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
938
+ registry_unref_list : Arc :: new ( Mutex :: new ( Some ( Vec :: new ( ) ) ) ) ,
924
939
} ) ) ;
925
940
* ( ffi:: lua_getextraspace ( state) as * mut * mut ExtraData ) = extra_data;
926
941
} ) ;
@@ -934,6 +949,11 @@ impl Lua {
934
949
935
950
fn create_callback_function < ' lua > ( & ' lua self , func : Callback < ' lua > ) -> Result < Function < ' lua > > {
936
951
unsafe extern "C" fn callback_call_impl ( state : * mut ffi:: lua_State ) -> c_int {
952
+ if ffi:: lua_type ( state, ffi:: lua_upvalueindex ( 1 ) ) == ffi:: LUA_TNIL {
953
+ ffi:: lua_pushstring ( state, cstr ! ( "rust callback has been destructed" ) ) ;
954
+ ffi:: lua_error ( state)
955
+ }
956
+
937
957
callback_error ( state, || {
938
958
let lua = Lua {
939
959
state : state,
@@ -976,7 +996,7 @@ impl Lua {
976
996
self . state ,
977
997
& FUNCTION_METATABLE_REGISTRY_KEY as * const u8 as * mut c_void ,
978
998
) ;
979
- ffi:: lua_gettable ( self . state , ffi:: LUA_REGISTRYINDEX ) ;
999
+ ffi:: lua_rawget ( self . state , ffi:: LUA_REGISTRYINDEX ) ;
980
1000
ffi:: lua_setmetatable ( self . state , -2 ) ;
981
1001
982
1002
protect_lua_call ( self . state , 1 , 1 , |state| {
@@ -988,9 +1008,106 @@ impl Lua {
988
1008
}
989
1009
}
990
1010
1011
+ fn do_create_userdata < T > ( & self , data : T ) -> Result < AnyUserData >
1012
+ where
1013
+ T : UserData ,
1014
+ {
1015
+ unsafe {
1016
+ stack_err_guard ( self . state , 0 , move || {
1017
+ check_stack ( self . state , 3 ) ;
1018
+
1019
+ push_userdata :: < RefCell < T > > ( self . state , RefCell :: new ( data) ) ?;
1020
+
1021
+ ffi:: lua_rawgeti (
1022
+ self . state ,
1023
+ ffi:: LUA_REGISTRYINDEX ,
1024
+ self . userdata_metatable :: < T > ( ) ? as ffi:: lua_Integer ,
1025
+ ) ;
1026
+
1027
+ ffi:: lua_setmetatable ( self . state , -2 ) ;
1028
+
1029
+ Ok ( AnyUserData ( self . pop_ref ( self . state ) ) )
1030
+ } )
1031
+ }
1032
+ }
1033
+
991
1034
unsafe fn extra ( & self ) -> * mut ExtraData {
992
1035
* ( ffi:: lua_getextraspace ( self . main_state ) as * mut * mut ExtraData )
993
1036
}
994
1037
}
995
1038
1039
+ impl < ' lua > Scope < ' lua > {
1040
+ pub fn create_function < ' scope , A , R , F > ( & ' scope self , mut func : F ) -> Result < Function < ' scope > >
1041
+ where
1042
+ A : FromLuaMulti < ' scope > ,
1043
+ R : ToLuaMulti < ' scope > ,
1044
+ F : ' scope + FnMut ( & ' scope Lua , A ) -> Result < R > ,
1045
+ {
1046
+ unsafe {
1047
+ let mut f = self . lua
1048
+ . create_callback_function ( Box :: new ( move |lua, args| {
1049
+ func ( lua, A :: from_lua_multi ( args, lua) ?) ?. to_lua_multi ( lua)
1050
+ } ) ) ?;
1051
+ f. 0 . drop_unref = false ;
1052
+ let mut destructors = self . destructors . borrow_mut ( ) ;
1053
+ let registry_id = f. 0 . registry_id ;
1054
+ destructors. push ( Box :: new ( move |state| {
1055
+ check_stack ( state, 2 ) ;
1056
+ ffi:: lua_rawgeti (
1057
+ state,
1058
+ ffi:: LUA_REGISTRYINDEX ,
1059
+ registry_id as ffi:: lua_Integer ,
1060
+ ) ;
1061
+ ffi:: lua_getupvalue ( state, -1 , 1 ) ;
1062
+ let ud = take_userdata :: < RefCell < Callback > > ( state) ;
1063
+
1064
+ ffi:: lua_pushnil ( state) ;
1065
+ ffi:: lua_setupvalue ( state, -2 , 1 ) ;
1066
+
1067
+ ffi:: lua_pop ( state, 1 ) ;
1068
+ Box :: new ( ud)
1069
+ } ) ) ;
1070
+ Ok ( f)
1071
+ }
1072
+ }
1073
+
1074
+ pub fn create_userdata < T > ( & self , data : T ) -> Result < AnyUserData >
1075
+ where
1076
+ T : UserData ,
1077
+ {
1078
+ unsafe {
1079
+ let mut u = self . lua . do_create_userdata ( data) ?;
1080
+ u. 0 . drop_unref = false ;
1081
+ let mut destructors = self . destructors . borrow_mut ( ) ;
1082
+ let registry_id = u. 0 . registry_id ;
1083
+ destructors. push ( Box :: new ( move |state| {
1084
+ check_stack ( state, 1 ) ;
1085
+ ffi:: lua_rawgeti (
1086
+ state,
1087
+ ffi:: LUA_REGISTRYINDEX ,
1088
+ registry_id as ffi:: lua_Integer ,
1089
+ ) ;
1090
+ Box :: new ( take_userdata :: < RefCell < T > > ( state) )
1091
+ } ) ) ;
1092
+ Ok ( u)
1093
+ }
1094
+ }
1095
+ }
1096
+
1097
+ impl < ' lua > Drop for Scope < ' lua > {
1098
+ fn drop ( & mut self ) {
1099
+ // We separate the action of invalidating the userdata in Lua and actually dropping the
1100
+ // userdata type into two phases. This is so that, in the event a userdata drop panics, we
1101
+ // can be sure that all of the userdata in Lua is actually invalidated.
1102
+
1103
+ let state = self . lua . state ;
1104
+ let to_drop = self . destructors
1105
+ . get_mut ( )
1106
+ . drain ( ..)
1107
+ . map ( |mut destructor| destructor ( state) )
1108
+ . collect :: < Vec < _ > > ( ) ;
1109
+ drop ( to_drop) ;
1110
+ }
1111
+ }
1112
+
996
1113
static FUNCTION_METATABLE_REGISTRY_KEY : u8 = 0 ;
0 commit comments