From 33bde97b66c03e05f99213b3e6741af6577f218a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E9=A3=8E?= Date: Wed, 29 Aug 2012 14:33:24 +0800 Subject: [PATCH] remote object --- Makefile | 4 +- lua-serialize/Makefile | 8 - lua-serialize/README | 1 - lua-serialize/test.lua | 28 --- lua-serialize/test2.lua | 15 -- lualib-src/lua-remoteobj.c | 180 +++++++++++++++ .../serialize.c => lualib-src/lua-seri.c | 206 +++++------------- .../luaseri.h => lualib-src/lua-seri.h | 0 lualib-src/lua-skynet.c | 23 +- lualib/skynet.lua | 74 +++++++ service/main.lua | 2 + service/remote_root.lua | 20 ++ service/testrobj.lua | 24 ++ 13 files changed, 378 insertions(+), 207 deletions(-) delete mode 100644 lua-serialize/Makefile delete mode 100644 lua-serialize/README delete mode 100644 lua-serialize/test.lua delete mode 100644 lua-serialize/test2.lua create mode 100644 lualib-src/lua-remoteobj.c rename lua-serialize/serialize.c => lualib-src/lua-seri.c (81%) rename lua-serialize/luaseri.h => lualib-src/lua-seri.h (100%) create mode 100644 service/remote_root.lua create mode 100644 service/testrobj.lua diff --git a/Makefile b/Makefile index 2f93ad57a..7e061a36c 100644 --- a/Makefile +++ b/Makefile @@ -48,8 +48,8 @@ service/snlua.so : service-src/service_lua.c service/gate.so : gate/mread.c gate/ringbuffer.c gate/main.c gcc $(CFLAGS) $(SHARED) $^ -o $@ -Igate -Iskynet-src -lualib/skynet.so : lualib-src/lua-skynet.c lua-serialize/serialize.c - gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src -Ilua-serialize +lualib/skynet.so : lualib-src/lua-skynet.c lualib-src/lua-seri.c lualib-src/lua-remoteobj.c + gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src service/client.so : service-src/service_client.c gcc $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src diff --git a/lua-serialize/Makefile b/lua-serialize/Makefile deleted file mode 100644 index e6ebf95c5..000000000 --- a/lua-serialize/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all : luaseri.so - -luaseri.so : serialize.c - gcc -Wall -fPIC -O2 -D LUAMODULE -o $@ --shared $^ - -clean : - rm luaseri.so - diff --git a/lua-serialize/README b/lua-serialize/README deleted file mode 100644 index 011225185..000000000 --- a/lua-serialize/README +++ /dev/null @@ -1 +0,0 @@ -see https://github.com/cloudwu/lua-serialize \ No newline at end of file diff --git a/lua-serialize/test.lua b/lua-serialize/test.lua deleted file mode 100644 index 6d787cdda..000000000 --- a/lua-serialize/test.lua +++ /dev/null @@ -1,28 +0,0 @@ -s = require "luaseri" - -a = s.pack { hello={3,4}, false, 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 } - -s.dump(a) - -a = s.append(a, 42,4.2,-1,1000,80000,"hello",true,false,nil,"1234567890123456789012345678901234567890") - -s.dump(a) -print(a) - -function pr(t,...) - for k,v in pairs(t) do - print(k,v) - end - print(...) -end - -print ("------") - -local seri, length = s.serialize(a) -print(seri, length) - -pr(s.unpack(a)) - -print("-------") - -pr(s.deserialize(seri, length)) diff --git a/lua-serialize/test2.lua b/lua-serialize/test2.lua deleted file mode 100644 index e2177179d..000000000 --- a/lua-serialize/test2.lua +++ /dev/null @@ -1,15 +0,0 @@ -local s = require "luaseri" - -addressbook = { - name = "Alice", - id = 12345, - phone = { - { number = "1301234567" }, - { number = "87654321", type = "WORK" }, - } -} - -for i=1,100000 do - local u = s.pack (addressbook) - local t = s.unpack(u) -end diff --git a/lualib-src/lua-remoteobj.c b/lualib-src/lua-remoteobj.c new file mode 100644 index 000000000..58707c8f9 --- /dev/null +++ b/lualib-src/lua-remoteobj.c @@ -0,0 +1,180 @@ +#include +#include + +#include +#include +#include + +#define MAX_REMOTE_OBJECT 0x40000 + +struct remote_address { + int handle; + uint32_t address; +}; + +struct remote_objects { + int handle_index; + struct remote_address addr[MAX_REMOTE_OBJECT]; +}; + +static struct remote_objects * _R = NULL; + +static struct remote_objects * +_create() { + struct remote_objects * r = malloc(sizeof(*r)); + r->handle_index = 0; + memset(r, 0, sizeof(*r)); + r->addr[0].address = 0xffffffff; + return r; +} + +static void +_remove(struct remote_objects *r, uint32_t address) { + int i; + for (i=0;iaddr[i].address == address) { + r->addr[i].address = 0; + } + } +} + +static int +_alloc(struct remote_objects *r, uint32_t address) { + int i = 0; + for (i=0;ihandle_index, 1); + struct remote_address * slot = r->addr + handle % MAX_REMOTE_OBJECT; + if (slot->address == 0) { + if (__sync_bool_compare_and_swap(&slot->address, 0, address)) { + slot->handle = handle; + return handle; + } + } + } + + return -1; +} + +static int +_bind(struct remote_objects *r, int handle, uint32_t address) { + struct remote_address * slot = r->addr + handle % MAX_REMOTE_OBJECT; + if (slot->handle != handle) { + return 0; + } + for (;;) { + uint32_t old = slot->address; + if (__sync_bool_compare_and_swap(&slot->address, old, address)) { + return old; + } + } +} + +static uint32_t +_query(struct remote_objects *r, int handle) { + struct remote_address * slot = r->addr + handle % MAX_REMOTE_OBJECT; + if (slot->handle == handle) { + return slot->address; + } + return 0; +} + +static uint32_t +_getaddr(lua_State *L, int index) { + size_t sz; + const char * addr = luaL_checklstring(L,index,&sz); + if (addr[0] != ':') { + luaL_error(L, "Invalid address %s",addr); + } + return strtoul(addr+1, NULL, 16); +} + +static void +_id_to_hex(char * str, uint32_t id) { + int i; + static char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; + str[0] = ':'; + for (i=0;i<8;i++) { + str[i+1] = hex[(id >> ((7-i) * 4))&0xf]; + } + str[9] = '\0'; +} + +static int +lbind(lua_State *L) { + uint32_t addr = lua_tounsigned(L,lua_upvalueindex(1)); + int handle = luaL_checkinteger(L,1); + uint32_t old = _bind(_R, handle, addr); + if (old == 0) { + luaL_error(L, "handle %d is not exist", handle); + } + char tmp[10]; + _id_to_hex(tmp, old); + lua_pushlstring(L,tmp,9); + return 1; +} + +static int +lremove(lua_State *L) { + uint32_t * addr = lua_touserdata(L,1); + _remove(_R, *addr); + return 0; +} + +static int +lalloc(lua_State *L) { + uint32_t addr = lua_tounsigned(L,lua_upvalueindex(1)); + int handle = _alloc(_R, addr); + if (handle < 0) { + return luaL_error(L, "Too many remote object"); + } + lua_pushinteger(L,handle); + return 1; +} + +static int +lquery(lua_State *L) { + int handle = luaL_checkinteger(L,1); + uint32_t addr = _query(_R, handle); + if (addr == 0) { + return 0; + } + char tmp[10]; + _id_to_hex(tmp, addr); + lua_pushlstring(L,tmp,9); + return 1; +} + +int +remoteobj_init(lua_State *L) { + if (_R == NULL) { + struct remote_objects * r = _create(); + if (!__sync_bool_compare_and_swap(&_R, NULL, r)) { + free(r); + } + } + + uint32_t address = _getaddr(L, -1); + lua_pushcfunction(L, lquery); + lua_pushnumber(L, address); + + uint32_t * addr = lua_newuserdata(L, sizeof(*addr)); + *addr = address; + lua_createtable(L, 0, 1); + lua_pushcfunction(L, lremove); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L,-2); + + lua_pushcclosure(L, lalloc, 2); + lua_pushnumber(L, address); + lua_pushcclosure(L, lbind, 1); + + return 3; +} + + + + + + + + diff --git a/lua-serialize/serialize.c b/lualib-src/lua-seri.c similarity index 81% rename from lua-serialize/serialize.c rename to lualib-src/lua-seri.c index f573a983d..d6aeb2c8a 100644 --- a/lua-serialize/serialize.c +++ b/lualib-src/lua-seri.c @@ -1,3 +1,7 @@ +/* + https://github.com/cloudwu/lua-serialize + */ + #include #include #include @@ -15,6 +19,7 @@ // hibits 0~31 : len #define TYPE_LONG_STRING 5 #define TYPE_TABLE 6 +#define TYPE_REMOTE 7 #define MAX_COOKIE 32 #define COMBINE_TYPE(t,v) ((t) | (v) << 3) @@ -123,20 +128,6 @@ rball_init(struct read_block * rb, char * buffer, int size) { rb->ptr = 0; } -#ifdef LUAMODULE - -static int -rb_init(struct read_block *rb, struct block *b) { - rb->buffer = NULL; - rb->current = b; - memcpy(&(rb->len),b->buffer,sizeof(rb->len)); - rb->ptr = sizeof(rb->len); - rb->len -= rb->ptr; - return rb->len; -} - -#endif - static void * rb_read(struct read_block *rb, void *buffer, int sz) { if (rb->len < sz) { @@ -215,26 +206,26 @@ wb_boolean(struct write_block *wb, int boolean) { } static inline void -wb_integer(struct write_block *wb, int v) { +wb_integer(struct write_block *wb, int v, int type) { if (v == 0) { - int n = COMBINE_TYPE(TYPE_NUMBER , 0); + int n = COMBINE_TYPE(type , 0); wb_push(wb, &n, 1); } else if (v<0) { - int n = COMBINE_TYPE(TYPE_NUMBER , 4); + int n = COMBINE_TYPE(type , 4); wb_push(wb, &n, 1); wb_push(wb, &v, 4); } else if (v<0x100) { - int n = COMBINE_TYPE(TYPE_NUMBER , 1); + int n = COMBINE_TYPE(type , 1); wb_push(wb, &n, 1); uint8_t byte = (uint8_t)v; wb_push(wb, &byte, 1); } else if (v<0x10000) { - int n = COMBINE_TYPE(TYPE_NUMBER , 2); + int n = COMBINE_TYPE(type , 2); wb_push(wb, &n, 1); uint16_t word = (uint16_t)v; wb_push(wb, &word, 2); } else { - int n = COMBINE_TYPE(TYPE_NUMBER , 4); + int n = COMBINE_TYPE(type , 4); wb_push(wb, &n, 1); wb_push(wb, &v, 4); } @@ -280,7 +271,7 @@ wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) { if (array_size > MAX_COOKIE-1) { int n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1); wb_push(wb, &n, 1); - wb_integer(wb, array_size); + wb_integer(wb, array_size,TYPE_NUMBER); } else { int n = COMBINE_TYPE(TYPE_TABLE, array_size); wb_push(wb, &n, 1); @@ -339,7 +330,7 @@ _pack_one(lua_State *L, struct write_block *b, int index, int depth) { lua_Integer x = lua_tointeger(L,index); lua_Number n = lua_tonumber(L,index); if ((lua_Number)x==n) { - wb_integer(b, x); + wb_integer(b, x, TYPE_NUMBER); } else { wb_number(b,n); } @@ -361,9 +352,21 @@ _pack_one(lua_State *L, struct write_block *b, int index, int depth) { case LUA_TLIGHTUSERDATA: wb_pointer(b, lua_touserdata(L,index)); break; - case LUA_TTABLE: - wb_table(L, b, index, depth+1); + case LUA_TTABLE: { + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + lua_pushvalue(L, lua_upvalueindex(2)); // __remote + lua_rawget(L, index); + if (lua_isnumber(L,-1)) { + int handle = lua_tointeger(L,-1); + lua_pop(L,1); + wb_integer(b, handle, TYPE_REMOTE); + } else { + wb_table(L, b, index, depth+1); + } break; + } default: wb_free(b); luaL_error(L, "Unsupport type %s to serialize", lua_typename(L, type)); @@ -379,30 +382,6 @@ _pack_from(lua_State *L, struct write_block *b, int from) { } } -#ifdef LUAMODULE - -static int -_pack(lua_State *L) { - struct write_block b; - wb_init(&b, NULL); - _pack_from(L,&b,0); - struct block * ret = wb_close(&b); - lua_pushlightuserdata(L,ret); - return 1; -} - -static int -_append(lua_State *L) { - struct write_block b; - wb_init(&b, lua_touserdata(L,1)); - _pack_from(L,&b,1); - struct block * ret = wb_close(&b); - lua_pushlightuserdata(L,ret); - return 1; -} - -#endif - static inline void __invalid_stream(lua_State *L, struct read_block *rb, int line) { int len = rb->len; @@ -414,8 +393,8 @@ __invalid_stream(lua_State *L, struct read_block *rb, int line) { #define _invalid_stream(L,rb) __invalid_stream(L,rb,__LINE__) -static double -_get_number(lua_State *L, struct read_block *rb, int cookie) { +static int +_get_integer(lua_State *L, struct read_block *rb, int cookie) { switch (cookie) { case 0: return 0; @@ -440,16 +419,22 @@ _get_number(lua_State *L, struct read_block *rb, int cookie) { _invalid_stream(L,rb); return *pn; } - case 8: { + default: + _invalid_stream(L,rb); + return 0; + } +} + +static double +_get_number(lua_State *L, struct read_block *rb, int cookie) { + if (cookie == 8) { double n = 0; double * pn = rb_read(rb,&n,8); if (pn == NULL) _invalid_stream(L,rb); return *pn; - } - default: - _invalid_stream(L,rb); - return 0; + } else { + return (double)_get_integer(L,rb,cookie); } } @@ -530,6 +515,23 @@ _push_value(lua_State *L, struct read_block *rb, int type, int cookie) { _unpack_table(L,rb,cookie); break; } + case TYPE_REMOTE: { + lua_pushvalue(L,lua_upvalueindex(1)); // metatable + int handle = _get_integer(L,rb,cookie); + lua_rawgeti(L,-1,handle); + if (lua_isnil(L,-1)) { + lua_pop(L,2); + lua_createtable(L,0,1); + lua_pushvalue(L,lua_upvalueindex(2)); // __remote + lua_pushinteger(L,handle); + lua_rawset(L,-3); + lua_pushvalue(L,lua_upvalueindex(1)); // metatable + lua_setmetatable(L,-2); + } else { + lua_replace(L, -2); + } + break; + } } } @@ -543,65 +545,6 @@ _unpack_one(lua_State *L, struct read_block *rb) { _push_value(L, rb, *t & 0x7, *t>>3); } -#ifdef LUAMODULE - -static int -_unpack(lua_State *L) { - struct block * blk = lua_touserdata(L,1); - if (blk == NULL) { - return luaL_error(L, "Need a block to unpack"); - } - lua_settop(L,0); - struct read_block rb; - rb_init(&rb, blk); - - int i; - for (i=0;;i++) { - if (i%16==15) { - lua_checkstack(L,i); - } - uint8_t type = 0; - uint8_t *t = rb_read(&rb, &type, 1); - if (t==NULL) - break; - _push_value(L, &rb, *t & 0x7, *t>>3); - } - - rb_close(&rb); - - return lua_gettop(L); -} - -static int -_dump_mem(const char * buffer, int len, int size) { - int i; - for (i=0;ibuffer ,sizeof(len)); - len -= sizeof(len); - printf("Len = %d\n",len); - len = _dump_mem(b->buffer + sizeof(len), BLOCK_SIZE - sizeof(len) , len); - while (len > 0) { - b=b->next; - len = _dump_mem(b->buffer, BLOCK_SIZE , len); - } - printf("\n"); - return 0; -} - -#endif - static void _seri(lua_State *L, struct block *b) { uint32_t len = 0; @@ -636,22 +579,6 @@ _seri(lua_State *L, struct block *b) { lua_pushinteger(L, sz); } -#ifdef LUAMODULE - -static int -_serialize(lua_State *L) { - struct block *b = lua_touserdata(L,1); - if (b==NULL) { - return luaL_error(L, "dump null pointer"); - } - - _seri(L,b); - - return 2; -} - -#endif - int _luaseri_unpack(lua_State *L) { if (lua_isnoneornil(L,1)) { @@ -700,22 +627,3 @@ _luaseri_pack(lua_State *L) { return 2; } - -#ifdef LUAMODULE - -int -luaopen_luaseri(lua_State *L) { - luaL_Reg l[] = { - { "pack", _pack }, - { "unpack", _unpack }, - { "append", _append }, - { "serialize", _serialize }, - { "deserialize", _luaseri_unpack }, - { "dump", _dump }, - { NULL, NULL }, - }; - luaL_newlib(L,l); - return 1; -} - -#endif \ No newline at end of file diff --git a/lua-serialize/luaseri.h b/lualib-src/lua-seri.h similarity index 100% rename from lua-serialize/luaseri.h rename to lualib-src/lua-seri.h diff --git a/lualib-src/lua-skynet.c b/lualib-src/lua-skynet.c index 80b1374c7..41a9d051f 100644 --- a/lualib-src/lua-skynet.c +++ b/lualib-src/lua-skynet.c @@ -1,5 +1,5 @@ #include "skynet.h" -#include "luaseri.h" +#include "lua-seri.h" #include #include @@ -184,9 +184,19 @@ _tostring(lua_State *L) { return 1; } +// define in lua-remoteobj.c +int remoteobj_init(lua_State *L); + int luaopen_skynet_c(lua_State *L) { luaL_checkversion(L); + + luaL_Reg pack[] = { + { "pack", _luaseri_pack }, + { "unpack", _luaseri_unpack }, + { NULL, NULL }, + }; + luaL_Reg l[] = { { "send" , _send }, { "genid", _genid }, @@ -195,11 +205,13 @@ luaopen_skynet_c(lua_State *L) { { "callback" , _callback }, { "error", _error }, { "tostring", _tostring }, - { "pack", _luaseri_pack }, - { "unpack", _luaseri_unpack }, { NULL, NULL }, }; - luaL_newlibtable(L,l); + + lua_createtable(L, 0, (sizeof(pack) + sizeof(l))/sizeof(luaL_Reg)-2); + lua_newtable(L); + lua_pushstring(L,"__remote"); + luaL_setfuncs(L,pack,2); lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context"); struct skynet_context * ctx = lua_touserdata(L,-1); @@ -209,5 +221,8 @@ luaopen_skynet_c(lua_State *L) { luaL_setfuncs(L,l,1); + lua_pushcfunction(L, remoteobj_init); + lua_setfield(L, -2, "remote_init"); + return 1; } diff --git a/lualib/skynet.lua b/lualib/skynet.lua index 82ae2b92c..dbe319f48 100644 --- a/lualib/skynet.lua +++ b/lualib/skynet.lua @@ -216,4 +216,78 @@ function skynet.newservice(name, ...) return handle end +------ remote object -------- + +do + local remote_query, remote_alloc, remote_bind = c.remote_init(skynet.self()) + local weak_meta = { __mode = "kv" } + local meta = getmetatable(c.unpack(c.pack({ __remote = 0 }))) + local remote_call_func = setmetatable({}, weak_meta) + setmetatable(meta, weak_meta) + + local _send = assert(c.send) + local _yield = coroutine.yield + local _pack = assert(c.pack) + local _unpack = assert(c.unpack) + local _local = skynet.self() + + function meta__index(t, method) + local f = remote_call_func[method] + if f == nil then + f = function(...) + local addr = remote_query(t.__remote) + local session = _send(addr, -1, _pack(t,method,...)) + local msg, sz = _yield("CALL", session) + return select(2,assert(_unpack(msg,sz))) + end + remote_call_func[method] = f + end + rawset(t,method,f) + return f + end + + -- prevent gc + meta.__index = meta__index + + meta.__newindex = error + + function skynet.remote_create(t, handle) + t = t or {} + if handle then + remote_bind(handle) + else + handle = remote_alloc() + end + rawset(t, "__remote" , handle) + rawset(meta, handle, t) + return t + end + + function skynet.remote_bind(handle) + return setmetatable( { __remote = handle } , meta) + end + + local function remote_call(obj, method, ...) + if type(obj) ~= "table" or type(method) ~= "string" then + return _yield("RETURN", _pack(false, "Invalid call")) + end + local f = obj[method] + if type(f) ~= "function" then + return _yield("RETURN", _pack(false, "Object has not method " .. method)) + end + return _yield("RETURN", _pack(pcall(f,...))) + end + + function skynet.remote_service(unknown) + local function f(msg,sz) + return remote_call(_unpack(msg,sz)) + end + c.callback(default_dispatch(f,unknown)) + end + + function skynet.remote_root() + return skynet.remote_bind(0) + end +end + return skynet diff --git a/service/main.lua b/service/main.lua index 44f68460f..30a96e8be 100644 --- a/service/main.lua +++ b/service/main.lua @@ -8,6 +8,8 @@ skynet.start(function() print("lualog",lualog) local launcher = skynet.launch("snlua","launcher") print("launcher", launcher) + local remoteroot = skynet.launch("snlua","remote_root") + print("remoteroot", remoteroot) local console = skynet.launch("snlua","console") print("console",console) local watchdog = skynet.launch("snlua","watchdog","8888 4 0") diff --git a/service/remote_root.lua b/service/remote_root.lua new file mode 100644 index 000000000..413157f2e --- /dev/null +++ b/service/remote_root.lua @@ -0,0 +1,20 @@ +local skynet = require "skynet" + +local service = {} +local root = {} + +function root.register(name, obj) + service[name] = obj +end + +function root.query(name) + return service[name] +end + +function root.echo(hello) + return hello +end + +skynet.remote_create(root,0) + +skynet.remote_service() \ No newline at end of file diff --git a/service/testrobj.lua b/service/testrobj.lua new file mode 100644 index 000000000..32ce341a7 --- /dev/null +++ b/service/testrobj.lua @@ -0,0 +1,24 @@ +local skynet = require "skynet" + +skynet.remote_service() + +local obj = { id = 0 } + +function obj.hello() + return "Hello" +end + +function obj:inc() + self.id = self.id + 1 + return self.id +end + +skynet.start(function() + skynet.remote_create(obj) + local root = skynet.remote_root() + print(root.echo("hello")) + root.register("test",obj) + local qobj = root.query "test" + print(qobj.hello()) + print(qobj:inc()) +end)