From a142eb1e1ee81919d10b55bb7fa2e33636098d85 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 7 Nov 2018 19:16:36 +0100 Subject: __lanesclone mechanism should actually work now --- CHANGES | 3 ++ deep_test/deep_test.c | 80 +++++++++++++++++++++++++++++++++------------ deep_test/deeptest.lua | 53 ++++++++++++++++++++---------- docs/index.html | 65 ++++++++++++++++++++++++++++++++---- src/lanes.c | 1 + src/tools.c | 89 +++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 230 insertions(+), 61 deletions(-) diff --git a/CHANGES b/CHANGES index be0e7d9..228038b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ CHANGES: +CHANGE 132: BGe 7-Nov-18 + * __lanesclone mechanism should actually work now + CHANGE 131: BGe 7-Nov-18 * Fix potential crash at application shutdown when deep userdata were created before Lanes is required diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c index 8f34fe5..4aac586 100644 --- a/deep_test/deep_test.c +++ b/deep_test/deep_test.c @@ -80,10 +80,7 @@ static void* deep_test_id( lua_State* L, enum eDeepOp op_) case eDO_metatable: { - lua_newtable( L); // mt - luaL_setfuncs( L, deep_mt, 0); // mt - lua_pushvalue( L, -1); // mt mt - lua_setfield( L, -2, "__index"); // mt + luaL_getmetatable( L, "deep"); // mt luaG_pushdeepversion( L); // mt version return NULL; } @@ -115,6 +112,16 @@ struct s_MyClonableUserdata // ################################################################################################ +static int clonable_set( lua_State* L) +{ + struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1); + lua_Integer i = lua_tointeger( L, 2); + self->val = i; + return 0; +} + +// ################################################################################################ + static int clonable_tostring(lua_State* L) { struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1); @@ -124,13 +131,34 @@ static int clonable_tostring(lua_State* L) // ################################################################################################ +static int clonable_gc( lua_State* L) +{ + struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1); + return 0; +} + +// ################################################################################################ + static int clonable_lanesclone( lua_State* L) { - // no need to set the metatable, the Lane copying mechanism will take care of it - struct s_MyClonableUserdata* self = lua_touserdata( L, 1); - struct s_MyClonableUserdata* to = lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); - memcpy( to, self, sizeof(struct s_MyClonableUserdata)); - return 1; + switch( lua_gettop( L)) + { + case 0: + lua_pushinteger( L, sizeof( struct s_MyClonableUserdata)); + return 1; + + case 2: + { + struct s_MyClonableUserdata* self = lua_touserdata( L, 1); + struct s_MyClonableUserdata* from = lua_touserdata( L, 2); + *self = *from; + return 0; + } + + default: + (void) luaL_error( L, "Lanes called clonable_lanesclone with unexpected parameters"); + } + return 0; } // ################################################################################################ @@ -138,9 +166,9 @@ static int clonable_lanesclone( lua_State* L) static luaL_Reg const clonable_mt[] = { { "__tostring", clonable_tostring}, - //{ "__gc", deep_gc}, + { "__gc", clonable_gc}, { "__lanesclone", clonable_lanesclone}, - //{ "set", deep_set}, + { "set", clonable_set}, { NULL, NULL } }; @@ -149,15 +177,7 @@ static luaL_Reg const clonable_mt[] = int luaD_new_clonable( lua_State* L) { lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); - if( luaL_getmetatable( L, "clonable") == LUA_TNIL) // u mt? - { - lua_pop( L, 1); // u - lua_newtable( L); // u mt - luaL_setfuncs( L, clonable_mt, 0); - lua_pushvalue(L, -1); // u mt mt - lua_setfield(L, -2, "__index"); // u mt - } - lua_setmetatable( L, -2); // u + luaL_setmetatable( L, "clonable"); return 1; } @@ -175,6 +195,24 @@ static luaL_Reg const deep_module[] = extern int __declspec(dllexport) luaopen_deep_test(lua_State* L) { - luaL_newlib( L, deep_module); + luaL_newlib( L, deep_module); // M + + // preregister the metatables for the types we can instanciate so that Lanes can know about them + if( luaL_newmetatable( L, "clonable")) // M mt + { + luaL_setfuncs( L, clonable_mt, 0); + lua_pushvalue(L, -1); // M mt mt + lua_setfield(L, -2, "__index"); // M mt + } + lua_setfield(L, -2, "__clonableMT"); // M + + if( luaL_newmetatable( L, "deep")) // mt + { + luaL_setfuncs( L, deep_mt, 0); + lua_pushvalue(L, -1); // mt mt + lua_setfield(L, -2, "__index"); // mt + } + lua_setfield(L, -2, "__deepMT"); // M + return 1; } diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua index 3b514dd..c0bbab4 100644 --- a/deep_test/deeptest.lua +++ b/deep_test/deeptest.lua @@ -1,22 +1,39 @@ --- create a deep-aware full userdata while Lanes isn't loaded -local dt = require "deep_test" -local deep = dt.new_deep() -deep:set(666) -print( deep) +local lanes = require("lanes").configure{ with_timers = false} +local l = lanes.linda "my linda" -local clonable = dt.new_clonable() +-- we will transfer userdata created by this module, so we need to make Lanes aware of it +local dt = lanes.require "deep_test" --- now load Lanes and see if that userdata is transferable ---[[ -local lanes = require("lanes").configure() -local l = lanes.linda "my linda" +local test_deep = true +local test_clonable = false + +local performTest = function( obj_) + obj_:set(666) + print( "immediate:", obj_) + + l:set( "key", obj_) + local out = l:get( "key") + print( "out of linda:", out) + + local g = lanes.gen( + "package" + , { + required = { "deep_test"} -- we will transfer userdata created by this module, so we need to make this lane aware of it + } + , function( obj_) + print( "in lane:", obj_) + return obj_ + end + ) + h = g( obj_) + local from_lane = h[1] + print( "from lane:", from_lane) +end -l:set( "key", deep) -local deep_out = l:get( "key") -print( deep_out) +if test_deep then + performTest( dt.new_deep()) +end -lanes.register() -l:set( "key", clonable) -local clonable_out = l:get( "key") -print( clonable_out) ---]] \ No newline at end of file +if test_clonable then + performTest( dt.new_clonable()) +end diff --git a/docs/index.html b/docs/index.html index aa12a36..9c76bef 100644 --- a/docs/index.html +++ b/docs/index.html @@ -70,7 +70,7 @@

- This document was revised on 4-Nov-18, and applies to version 3.13. + This document was revised on 7-Nov-18, and applies to version 3.13.

@@ -1516,15 +1516,68 @@ events to a common Linda, but... :).
 static int clonable_lanesclone( lua_State* L)
 {
-	// no need to set the metatable, the Lane copying mechanism will take care of it
-	struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
-	struct s_MyClonableUserdata* to = lua_newuserdata( L, sizeof( struct s_MyClonableUserdata));
-	memcpy( to, self, sizeof(struct s_MyClonableUserdata));
+	switch( lua_gettop( L))
+	{
+		case 0:
+		lua_pushinteger( L, sizeof( struct s_MyClonableUserdata));
+		return 1;
+
+		case 2:
+		{
+			struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
+			struct s_MyClonableUserdata* from = lua_touserdata( L, 2);
+			*self = *from;
+			return 0;
+		}
+
+		default:
+		(void) luaL_error( L, "Lanes called clonable_lanesclone with unexpected parameters");
+	}
+	return 0;
+}
+
+

+ +

+ Of course, more complex objects may require smarter cloning behavior than a simple memcpy. Also, the module initialisation code should make each metatable accessible from the module table itself as in: +
+int luaopen_deep_test(lua_State* L)
+{
+	luaL_newlib( L, deep_module);
+
+	// preregister the metatables for the types we can instanciate so that Lanes can know about them
+	if( luaL_newmetatable( L, "clonable"))
+	{
+		luaL_setfuncs( L, clonable_mt, 0);
+		lua_pushvalue(L, -1);
+		lua_setfield(L, -2, "__index");
+	}
+	lua_setfield(L, -2, "__clonableMT"); // actual name is not important
+
+	if( luaL_newmetatable( L, "deep"))
+	{
+		luaL_setfuncs( L, deep_mt, 0);
+		lua_pushvalue(L, -1);
+		lua_setfield(L, -2, "__index");
+	}
+	lua_setfield(L, -2, "__deepMT"); // actual name is not important
+
+	return 1;
+}
+
+

+ +

+ Then a new clonable userdata instance can just do like any non-Lanes aware userdata, as long as its metatable contains the aforementionned __lanesclone method. Note that the current implementation doesn't support uservalues on such userdata. +
+int luaD_new_clonable( lua_State* L)
+{
+	lua_newuserdata( L, sizeof( struct s_MyClonableUserdata));
+	luaL_setmetatable( L, "clonable");
 	return 1;
 }
 

- Of course, more complex objects may require smarter cloning behavior than a simple memcpy.

Deep userdata in your own apps

diff --git a/src/lanes.c b/src/lanes.c index d4b4c73..c3e64fb 100644 --- a/src/lanes.c +++ b/src/lanes.c @@ -2289,6 +2289,7 @@ LUAG_FUNC( lane_new) // require the module in the target state, and populate the lookup table there too size_t len; char const* name = lua_tolstring( L, -1, &len); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name)); // require the module in the target lane lua_getglobal( L2, "require"); // require()? diff --git a/src/tools.c b/src/tools.c index 2f9de7b..8e886b5 100644 --- a/src/tools.c +++ b/src/tools.c @@ -825,6 +825,12 @@ static int table_lookup_sentinel( lua_State* L) return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); } +// function sentinel used to transfer cloned full userdata from/to keeper states +static int userdata_clone_sentinel( lua_State* L) +{ + return luaL_error( L, "userdata clone sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); +} + /* * retrieve the name of a function/table in the lookup database */ @@ -838,7 +844,7 @@ static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, c if( mode_ == eLM_FromKeeper) { lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! - if( f == func_lookup_sentinel || f == table_lookup_sentinel) + if( f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) { lua_getupvalue( L, i, 1); // ... v ... "f.q.n" } @@ -1594,6 +1600,7 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu bool_t ignore = FALSE; int val_type = lua_type( L, i); STACK_GROW( L2, 1); + STACK_CHECK( L); // L // L2 STACK_CHECK( L2); // L // L2 /* Skip the object if it has metatable with { __lanesignore = true } */ @@ -1662,30 +1669,53 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu { break; } + STACK_MID( L, 0); if( lua_getmetatable( L, i)) // ... mt? { - lua_getfield( L, -1, "__lanesclone"); // ... mt clone? - if( !lua_isnil( L, -1)) + lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? + if( lua_isnil( L, -1)) + { + lua_pop( L, 2); // ... + } + else { + FuncSubType fst; + lua_CFunction cloneFunc = luaG_tocfunction( L, -1, &fst); + size_t userdata_size = 0; void* const source = lua_touserdata( L, i); + void* clone = NULL; + lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone + // call the cloning function with 0 arguments, should return the number of bytes to allocate for the clone + lua_call( L, 0, 1); // ... mt __lanesclone size + STACK_MID( L, 3); + userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size + lua_pop( L, 1); // ... mt __lanesclone + clone = lua_newuserdata( L2, userdata_size); // ... u + // call cloning function in source state to perform the actual memory cloning + lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone + lua_pushlightuserdata( L, source); // ... mt __lanesclone source + lua_call( L, 2, 0); // ... mt + STACK_MID( L, 1); // copy the metatable in the target state - if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... mt? + if( inter_copy_one_( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u mt? { - // retrieve cloning function - lua_getfield( L2, -1, "__lanesclone"); // ... mt clone - lua_pushlightuserdata( L2, source); // ... mt clone p - // cloning function should create a new full userdata without a metatable - if( lua_pcall( L2, 1, 1, 0) == LUA_OK) // ... mt u + lua_pop( L, 1); // ... + STACK_MID( L, 0); + // when writing to a keeper state, we have here a sentinel function with the metatable's fqn as upvalue + if( eLM_ToKeeper == mode_) // ... u sentinel { - lua_insert( L2, -2); // ... u mt - lua_setmetatable( L2, -2); // ... u + ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); + // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn + lua_getupvalue( L2, -1, 1); // ... u sentinel fqn + lua_remove( L2, -2); // ... u fqn + lua_insert( L2, -2); // ... fqn u + lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel } - else // ... mt err + else // from keeper or direct, we have the userdata and the metatable { - // propagate any error to the source state - char const* errmsg = lua_tostring( L2, -1); - (void) luaL_error( L, "can't copy non-deep full userdata across lanes: %s", errmsg); + ASSERT_L( lua_istable( L2, -1)); + lua_setmetatable( L2, -2); // ... u } } else @@ -1693,7 +1723,6 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu (void) luaL_error( L, "Error copying a metatable"); } } - lua_pop( L, 2); // ... break; } @@ -1724,6 +1753,33 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu ret = FALSE; break; } + + if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata + { + // clone the full userdata again + size_t userdata_size = 0; + void* source; + void* clone; + // this function has 2 upvalues: the fqn of its metatable, and the userdata itself + lua_getupvalue( L, i, 2); // ... u + source = lua_touserdata( L, -1); + lookup_table( L2, L, i, mode_, upName_); // ... u // ... mt + // __lanesclone should always exist because we woudln't be restoring data from a userdata_clone_sentinel closure to begin with + lua_getfield( L2, -1, "__lanesclone"); // // ... mt __lanesclone + lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone + // call the cloning function with 0 arguments, should return the number of bytes to allocate for the clone + lua_call( L2, 0, 1); // ... mt __lanesclone size + userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size + lua_pop( L2, 1); // ... mt __lanesclone + clone = lua_newuserdata( L2, userdata_size); // ... mt __lanesclone u + lua_insert( L2, -3); // ... u mt __lanesclone + lua_pushlightuserdata( L2, clone); // ... u mt __lanesclone clone + lua_pushlightuserdata( L2, source); // ... u mt __lanesclone clone source + lua_call( L2, 2, 0); // ... u mt + lua_setmetatable( L2, -2); // ... u + lua_pop( L, 1); // ... + } + else { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "FUNCTION %s\n" INDENT_END, upName_)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); @@ -1802,6 +1858,7 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu } STACK_END( L2, ret ? 1 : 0); + STACK_END( L, 0); return ret; } -- cgit v1.2.3-55-g6feb