LuaLanes / lanes

Lanes is a lightweight, native, lazy evaluating multithreading library for Lua 5.1 to 5.4.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Suggestion: use lua_objlen to get size of userdata for __lanesclone

jjvbsag opened this issue · comments

In our project we have diffent userdata with different sizes, which share the same metatable. By this and the current approach of __lanesclone metamethod it is not possible to implement lanesclone, because the function only gets lightuserdata without size information.

Now lua_objlen always returns the size of an allocated userdata, so we changed and simplified the implementation.
Our change to tools.c:

Index: lanes-3.15.1/src/tools.c
===================================================================
--- lanes-3.15.1/src/tools.c	(Revision 449609)
+++ lanes-3.15.1/src/tools.c	(Arbeitskopie)
@@ -1473,6 +1473,7 @@
 
 static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
 {
+	int const sourceidx = lua_absindex( L, i);
 	void* const source = lua_touserdata( L, i);
 
 	STACK_CHECK( L, 0);
@@ -1512,13 +1513,7 @@
 		int const mt = lua_absindex( L, -2);
 		size_t userdata_size = 0;
 		void* clone = NULL;
-		lua_pushvalue( L, -1);                                                 // ... mt __lanesclone __lanesclone
-		// call the cloning function with 1 argument, should return the number of bytes to allocate for the clone
-		lua_pushlightuserdata( L, source);                                     // ... mt __lanesclone __lanesclone source
-		lua_call( L, 1, 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
+		userdata_size = lua_objlen(L,sourceidx);
 		// we need to copy over the uservalues of the userdata as well
 		{
 			// extract all the uservalues, but don't transfer them yet
@@ -1582,7 +1577,8 @@
 			// 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 clone source
-			lua_call( L, 2, 0);                                                  // ... mt
+			lua_pushinteger(L,userdata_size);
+			lua_call( L, 3, 0);                                                  // ... mt
 			STACK_MID( L, 1);
 		}
 	}
@@ -1677,15 +1673,11 @@
 		lookup_table( L2, L, i, mode_, upName_);                                                               // ... mt
 		// __lanesclone should always exist because we wouldn'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
 		// 'i' slot is the closure, but from now on it is the actual userdata
 		i = lua_gettop( L);
 		source = lua_touserdata( L, -1);
 		// call the cloning function with 1 argument, should return the number of bytes to allocate for the clone
-		lua_pushlightuserdata( L2, source);                                                                    // ... mt __lanesclone __lanesclone source
-		lua_call( L2, 1, 1);                                                                                   // ... mt __lanesclone size
-		userdata_size = (size_t) lua_tointeger( L2, -1);                                                       // ... mt __lanesclone size
-		lua_pop( L2, 1);                                                                                       // ... mt __lanesclone
+		userdata_size = lua_objlen(L,-1);
 		{
 			// extract uservalues (don't transfer them yet)
 			int uvi = 0;
@@ -1723,7 +1715,8 @@
 		lua_replace( L2, -3);                                                                                  // ... u __lanesclone
 		lua_pushlightuserdata( L2, clone);                                                                     // ... u __lanesclone clone
 		lua_pushlightuserdata( L2, source);                                                                    // ... u __lanesclone clone source
-		lua_call( L2, 2, 0);                                                                                   // ... u
+		lua_pushinteger( L2, userdata_size);                                                                    // ... u __lanesclone clone source
+		lua_call( L2, 3, 0);                                                                                   // ... u
 	}
 	else
 	{

By doing this there is no need to call __lanesclone to get userdata_size and we can pass userdata_size as third argument to __lanesclone, so a generic __lanesclone implementation could be:

int luaU_lanesclone( lua_State* L)
{
	switch( lua_gettop( L))
	{
		case 3:
		{
			void*self = lua_touserdata( L, 1);
			void*from = lua_touserdata( L, 2);
			int userdata_size=lua_tointeger(L,3);
			memcpy(self,from,userdata_size);
			return 0;
		}

		default:
		(void) luaL_error( L, "Lanes called luaU_lanesclone with unexpected parameters");
	}
	return 0;
}

Of course this can be specializes, if memcpy is not good enough for the data inside the userdata object, but for plain old c struchtures, this is more than sufficient.

Please review the change, your feedback is welcome

Looks good to me, I'll give it a try. But we agree that this is an API-breaking change.

Looks good to me, I'll give it a try. But we agree that this is an API-breaking change. However, since lua_objlen was renamed lua_rawlen in Lua 5.2 I have to take care to keep compatibility with all current supported versions.

FYI: We are using lanes on an embedded arm-system (ARMv7) with LuaJIT 2.0.5 and the patched lanes-3.15.1 works like a charm

Give a try to 2c0000d.

I'm quite busy on the job, will try next week. Thanks & Regards

I've fixed more little things related to the way I use the memory allocator in conjunction with LuaJIT. Hopefully it should run fine now. I've tested a few of the test scripts with LuaJIT 2.0.5 and 2.1.0-beta3 as well as PUC-Lua, all seems fine now.