Lua RPC and userdata

1k views Asked by At

I'm currently using luarpc in my program to make interprocess communication. The problem now is that due to my tolua++ binding which stores class instances as userdata im unable to use any of those functions cause luarpc cant handle userdata. My question now is if would be possible (and how) to transmit userdata if you know that its always only a pointer (4 Bytes) and has a metatable attached for call and indexing operations.

2

There are 2 answers

0
ACB On BEST ANSWER

Ok i got it working now. What i did is for userdata args/returns i send the actual ptr + metatable name(typename) to the client. the client then attaches a metatable with an __index method that creates a new helper with the typename and appends a helper with the field you want to access. when you then call or read a field from that userdata the client sends the data for calling a field of the typetable and the userdata to the server.

ReadVariable:

    lua_pushlightuserdata(L,msg.read<void*>());
#ifndef RPC_SERVER
    luaL_getmetatable(L,"rpc.userdata");
    int len = msg.read<int>();
    char* s = new char[len];
    msg.read((uint8*)s,len);
    s[len] = '\0';
    lua_pushlstring(L,s,len);
    lua_setfield(L,-2,"__name");
    lua_pushlightuserdata(L,TlsGetValue(transporttls));
    lua_setfield(L,-2,"__transport");
    lua_setmetatable(L,-2);
#endif

Write Variable:

    else
    {
        msg.append<RPCType>(RPC_USERDATA);
        msg.append<void*>(lua_touserdata(L,idx));
#ifdef RPC_SERVER
        lua_getmetatable(L,idx);
        lua_rawget(L,LUA_REGISTRYINDEX);
        const char* s = lua_tostring(L,-1);
        int len = lua_strlen(L,-1);
        msg.append<int>(len);
        msg.append(s,len);
#endif
        lua_settop(L,stack_at_start);
    }

userdata indexing:

checkNumArgs(L,2);
ASSERT(lua_isuserdata(L,1) && isMetatableType(L,1,"rpc.userdata"));

if(lua_type(L,2) != LUA_TSTRING)
    return luaL_error( L, "can't index a handle with a non-string" );
const char* s = lua_tostring(L,2);
if(strlen(s) > MAX_PATH - 1)
    return luaL_error(L,"string to long");

int stack = lua_gettop(L);
lua_getmetatable(L,1);
lua_getfield(L,-1,"__name");
const char* name = lua_tostring(L,-1);
if(strlen(name) > MAX_PATH - 1)
    return luaL_error(L,"string to long");
lua_pop(L,1); // remove name

lua_getfield(L,-1,"__transport");
Transport* t = reinterpret_cast<Transport*>(lua_touserdata(L,-1));
lua_pop(L,1);

Helper* h  = Helper::create(L,t,name);
Helper::append(L,h,s);
return 1;

well i more or less rewrote the complete rpc library to work with named pipes and windows but i think the code should give anyone enough information to implement it.

this allows code like:

local remote = rpc.remoteobj:getinstance()
remote:dosmthn()

on the clientside. it currently doesnt allow to add new fields but well this is all i need for now :D

5
Nicol Bolas On

You can't.

It doesn't matter if the userdata is a pointer or an object. The reason you can't arbitrarily RPC through them is because the data is not stored in Lua. And therefore LuaRPC cannot transmit it properly.

A pointer into your address space is absolutely worthless for some other process; even moreso if it's running on another machine. You have to actually transmit the data itself to make the RPC work. LuaRPC can do this transmission, but only for data that it can understand. And the only data it understands is data stored in Lua.