diff --git a/Makefile b/Makefile index 63d9085d..1bcb3603 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ jemalloc : $(MALLOC_STATICLIB) CSERVICE = snlua logger gate harbor LUA_CLIB = skynet socketdriver int64 bson mongo md5 netpack \ cjson clientsocket memory profile multicast \ - cluster crypt + cluster crypt sharedata SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \ skynet_server.c skynet_start.c skynet_timer.c skynet_error.c \ @@ -113,6 +113,9 @@ $(LUA_CLIB_PATH)/cluster.so : lualib-src/lua-cluster.c | $(LUA_CLIB_PATH) $(LUA_CLIB_PATH)/crypt.so : lualib-src/lua-crypt.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) $^ -o $@ +$(LUA_CLIB_PATH)/sharedata.so : lualib-src/lua-sharedata.c | $(LUA_CLIB_PATH) + $(CC) $(CFLAGS) $(SHARED) $^ -o $@ + clean : rm -f $(SKYNET_BUILD_PATH)/skynet $(CSERVICE_PATH)/*.so $(LUA_CLIB_PATH)/*.so diff --git a/examples/share.lua b/examples/share.lua new file mode 100644 index 00000000..2c81f790 --- /dev/null +++ b/examples/share.lua @@ -0,0 +1,63 @@ +local skynet = require "skynet" +local sharedata = require "sharedata" + +local mode = ... + +if mode == "host" then + +skynet.start(function() + skynet.error("new foobar") + sharedata.new("foobar", { a=1, b= { "hello", "world" } }) + + skynet.fork(function() + skynet.sleep(300) -- sleep 3s + skynet.error("update foobar") + sharedata.update("foobar", { a =2 }) + end) +end) + +else + + +skynet.start(function() + skynet.newservice(SERVICE_NAME, "host") + + local obj = sharedata.query "foobar" + + local b = obj.b + skynet.error(string.format("a=%d", obj.a)) + + for k,v in ipairs(b) do + skynet.error(string.format("b[%d]=%s", k,v)) + end + + for i = 1, 5 do + skynet.sleep(100) + skynet.error(i) + for k,v in pairs(obj) do + skynet.error(string.format("%s = %s", k , tostring(v))) + end + end + + local ok, err = pcall(function() + local tmp = { b[1], b[2] } -- b is invalid , so pcall should failed + end) + + if not ok then + skynet.error(err) + end + + collectgarbage() + skynet.error("sleep") + skynet.sleep(100) + b = nil + collectgarbage() + skynet.error("sleep") + skynet.sleep(100) + + sharedata.delete "foobar" + + skynet.exit() +end) + +end diff --git a/lualib-src/lua-sharedata.c b/lualib-src/lua-sharedata.c new file mode 100644 index 00000000..1a0f4f74 --- /dev/null +++ b/lualib-src/lua-sharedata.c @@ -0,0 +1,789 @@ +// build: gcc -O2 -Wall --shared -o conf.so luaconf.c + +#include +#include +#include +#include +#include +#include + +#define KEYTYPE_INTEGER 0 +#define KEYTYPE_STRING 1 + +#define VALUETYPE_NIL 0 +#define VALUETYPE_NUMBER 1 +#define VALUETYPE_STRING 2 +#define VALUETYPE_BOOLEAN 3 +#define VALUETYPE_TABLE 4 + +struct table; + +union value { + lua_Number n; + struct table * tbl; + int string; + int boolean; +}; + +struct node { + union value v; + int key; // integer key or index of string table + int next; // next slot index + uint32_t keyhash; + uint8_t keytype; // key type must be integer or string + uint8_t valuetype; // value type can be number/string/boolean/table + uint8_t nocolliding; // 0 means colliding slot +}; + +struct table; + +struct state { + int dirty; + int ref; + struct table * root; +}; + +struct table { + int sizearray; + int sizehash; + uint8_t *arraytype; + union value * array; + struct node * hash; + lua_State * L; +}; + +struct context { + lua_State * L; + struct table * tbl; + int string_index; +}; + +struct ctrl { + struct table * root; + struct table * update; +}; + +static int +countsize(lua_State *L, int sizearray) { + int n = 0; + lua_pushnil(L); + while (lua_next(L, 1) != 0) { + int type = lua_type(L, -2); + ++n; + if (type == LUA_TNUMBER) { + lua_Number key = lua_tonumber(L, -2); + int nkey = (int)key; + if ((lua_Number)nkey != key) { + luaL_error(L, "Invalid key %f", key); + } + if (nkey > 0 && nkey <= sizearray) { + --n; + } + } else if (type != LUA_TSTRING && type != LUA_TTABLE) { + luaL_error(L, "Invalid key type %s", lua_typename(L, type)); + } + lua_pop(L, 1); + } + + return n; +} + +static uint32_t +calchash(const char * str, size_t l) { + uint32_t h = (uint32_t)l; + size_t l1; + size_t step = (l >> 5) + 1; + for (l1 = l; l1 >= step; l1 -= step) { + h = h ^ ((h<<5) + (h>>2) + (uint8_t)(str[l1 - 1])); + } + return h; +} + +static int +stringindex(struct context *ctx, const char * str, size_t sz) { + lua_State *L = ctx->L; + lua_pushlstring(L, str, sz); + lua_pushvalue(L, -1); + lua_rawget(L, 1); + int index; + // stringmap(1) str index + if (lua_isnil(L, -1)) { + index = ++ctx->string_index; + lua_pop(L, 1); + lua_pushinteger(L, index); + lua_rawset(L, 1); + } else { + index = lua_tointeger(L, -1); + lua_pop(L, 2); + } + return index; +} + +static int convtable(lua_State *L); + +static void +setvalue(struct context * ctx, lua_State *L, int index, struct node *n) { + int vt = lua_type(L, index); + switch(vt) { + case LUA_TNIL: + n->valuetype = VALUETYPE_NIL; + break; + case LUA_TNUMBER: + n->v.n = lua_tonumber(L, index); + n->valuetype = VALUETYPE_NUMBER; + break; + case LUA_TSTRING: { + size_t sz = 0; + const char * str = lua_tolstring(L, index, &sz); + n->v.string = stringindex(ctx, str, sz); + n->valuetype = VALUETYPE_STRING; + break; + } + case LUA_TBOOLEAN: + n->v.boolean = lua_toboolean(L, index); + n->valuetype = VALUETYPE_BOOLEAN; + break; + case LUA_TTABLE: { + struct table *tbl = ctx->tbl; + ctx->tbl = (struct table *)malloc(sizeof(struct table)); + if (ctx->tbl == NULL) { + ctx->tbl = tbl; + luaL_error(L, "memory error"); + // never get here + } + memset(ctx->tbl, 0, sizeof(struct table)); + int absidx = lua_absindex(L, index); + + lua_pushcfunction(L, convtable); + lua_pushvalue(L, absidx); + lua_pushlightuserdata(L, ctx); + + lua_call(L, 2, 0); + + n->v.tbl = ctx->tbl; + n->valuetype = VALUETYPE_TABLE; + + ctx->tbl = tbl; + + break; + } + default: + luaL_error(L, "Unsupport value type %s", lua_typename(L, vt)); + break; + } +} + +static void +setarray(struct context *ctx, lua_State *L, int index, int key) { + struct node n; + setvalue(ctx, L, index, &n); + struct table *tbl = ctx->tbl; + --key; // base 0 + tbl->arraytype[key] = n.valuetype; + tbl->array[key] = n.v; +} + +static int +ishashkey(struct context * ctx, lua_State *L, int index, int *key, uint32_t *keyhash, int *keytype) { + int sizearray = ctx->tbl->sizearray; + int kt = lua_type(L, index); + if (kt == LUA_TNUMBER) { + *key = lua_tointeger(L, index); + if (*key > 0 && *key <= sizearray) { + return 0; + } + *keyhash = (uint32_t)*key; + *keytype = KEYTYPE_INTEGER; + } else { + size_t sz = 0; + const char * s = lua_tolstring(L, index, &sz); + *keyhash = calchash(s, sz); + *key = stringindex(ctx, s, sz); + *keytype = KEYTYPE_STRING; + } + return 1; +} + +static void +fillnocolliding(lua_State *L, struct context *ctx) { + struct table * tbl = ctx->tbl; + lua_pushnil(L); + while (lua_next(L, 1) != 0) { + int key; + int keytype; + uint32_t keyhash; + if (!ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) { + setarray(ctx, L, -1, key); + } else { + struct node * n = &tbl->hash[keyhash % tbl->sizehash]; + if (n->valuetype == VALUETYPE_NIL) { + n->key = key; + n->keytype = keytype; + n->keyhash = keyhash; + n->next = -1; + n->nocolliding = 1; + setvalue(ctx, L, -1, n); // set n->v , n->valuetype + } + } + lua_pop(L,1); + } +} + +static void +fillcolliding(lua_State *L, struct context *ctx) { + struct table * tbl = ctx->tbl; + int sizehash = tbl->sizehash; + int emptyslot = 0; + int i; + lua_pushnil(L); + while (lua_next(L, 1) != 0) { + int key; + int keytype; + uint32_t keyhash; + if (ishashkey(ctx, L, -2, &key, &keyhash, &keytype)) { + struct node * mainpos = &tbl->hash[keyhash % tbl->sizehash]; + if (!(mainpos->keytype == keytype && mainpos->key == key)) { + // the key has not insert + struct node * n = NULL; + for (i=emptyslot;ihash[i].valuetype == VALUETYPE_NIL) { + n = &tbl->hash[i]; + break; + } + } + assert(n); + n->next = mainpos->next; + mainpos->next = n - tbl->hash; + mainpos->nocolliding = 0; + n->key = key; + n->keytype = keytype; + n->keyhash = keyhash; + n->nocolliding = 0; + setvalue(ctx, L, -1, n); // set n->v , n->valuetype + } + } + lua_pop(L,1); + } +} + +// table need convert +// struct context * ctx +static int +convtable(lua_State *L) { + int i; + struct context *ctx = lua_touserdata(L,2); + struct table *tbl = ctx->tbl; + + tbl->L = ctx->L; + + int sizearray = lua_rawlen(L, 1); + if (sizearray) { + tbl->arraytype = (uint8_t *)malloc(sizearray * sizeof(uint8_t)); + if (tbl->arraytype == NULL) { + goto memerror; + } + for (i=0;iarraytype[i] = VALUETYPE_NIL; + } + tbl->array = (union value *)malloc(sizearray * sizeof(union value)); + if (tbl->array == NULL) { + goto memerror; + } + tbl->sizearray = sizearray; + } + int sizehash = countsize(L, sizearray); + if (sizehash) { + tbl->hash = (struct node *)malloc(sizehash * sizeof(struct node)); + if (tbl->hash == NULL) { + goto memerror; + } + for (i=0;ihash[i].valuetype = VALUETYPE_NIL; + tbl->hash[i].nocolliding = 0; + } + tbl->sizehash = sizehash; + + fillnocolliding(L, ctx); + fillcolliding(L, ctx); + } else { + int i; + for (i=1;i<=sizearray;i++) { + lua_rawgeti(L, 1, i); + setarray(ctx, L, -1, i); + lua_pop(L,1); + } + } + + return 0; +memerror: + return luaL_error(L, "memory error"); +} + +static void +delete_tbl(struct table *tbl) { + int i; + for (i=0;isizearray;i++) { + if (tbl->arraytype[i] == VALUETYPE_TABLE) { + delete_tbl(tbl->array[i].tbl); + } + } + for (i=0;isizehash;i++) { + if (tbl->hash[i].valuetype == VALUETYPE_TABLE) { + delete_tbl(tbl->hash[i].v.tbl); + } + } + free(tbl->arraytype); + free(tbl->array); + free(tbl->hash); + free(tbl); +} + +static int +pconv(lua_State *L) { + struct context *ctx = lua_touserdata(L,1); + lua_State * pL = lua_touserdata(L, 2); + int ret; + + lua_settop(L, 0); + + // init L (may throw memory error) + // create a table for string map + lua_newtable(L); + + lua_pushcfunction(pL, convtable); + lua_pushvalue(pL,1); + lua_pushlightuserdata(pL, ctx); + + ret = lua_pcall(pL, 2, 0, 0); + if (ret != LUA_OK) { + size_t sz = 0; + const char * error = lua_tolstring(pL, -1, &sz); + lua_pushlstring(L, error, sz); + lua_error(L); + // never get here + } + + luaL_checkstack(L, ctx->string_index + 3, NULL); + lua_settop(L,1); + + return 1; +} + +static void +convert_stringmap(struct context *ctx, struct table *tbl) { + lua_State *L = ctx->L; + lua_settop(L, ctx->string_index + 1); + lua_pushvalue(L, 1); + struct state * s = lua_newuserdata(L, sizeof(*s)); + s->dirty = 0; + s->ref = 0; + s->root = tbl; + lua_replace(L, 1); + lua_replace(L, -2); + + lua_pushnil(L); + // ... stringmap nil + while (lua_next(L, -2) != 0) { + int idx = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_pushvalue(L, -1); + lua_replace(L, idx); + } + + lua_pop(L, 1); + + lua_gc(L, LUA_GCCOLLECT, 0); +} + +static int +lnewconf(lua_State *L) { + int ret; + struct context ctx; + struct table * tbl = NULL; + luaL_checktype(L,1,LUA_TTABLE); + ctx.L = luaL_newstate(); + ctx.tbl = NULL; + ctx.string_index = 1; // 1 reserved for dirty flag + if (ctx.L == NULL) { + lua_pushliteral(L, "memory error"); + goto error; + } + tbl = (struct table *)malloc(sizeof(struct table)); + if (tbl == NULL) { + // lua_pushliteral may fail because of memory error, close first. + lua_close(ctx.L); + ctx.L = NULL; + lua_pushliteral(L, "memory error"); + goto error; + } + memset(tbl, 0, sizeof(struct table)); + ctx.tbl = tbl; + + lua_pushcfunction(ctx.L, pconv); + lua_pushlightuserdata(ctx.L , &ctx); + lua_pushlightuserdata(ctx.L , L); + + ret = lua_pcall(ctx.L, 2, 1, 0); + + if (ret != LUA_OK) { + size_t sz = 0; + const char * error = lua_tolstring(ctx.L, -1, &sz); + lua_pushlstring(L, error, sz); + goto error; + } + + convert_stringmap(&ctx, tbl); + + lua_pushlightuserdata(L, tbl); + + return 1; +error: + if (ctx.L) { + lua_close(ctx.L); + } + if (tbl) { + delete_tbl(tbl); + } + lua_error(L); + return -1; +} + +static struct table * +get_table(lua_State *L, int index) { + struct table *tbl = lua_touserdata(L,index); + if (tbl == NULL) { + luaL_error(L, "Need a conf object"); + } + return tbl; +} + +static int +ldeleteconf(lua_State *L) { + struct table *tbl = get_table(L,1); + lua_close(tbl->L); + delete_tbl(tbl); + return 0; +} + +static void +pushvalue(lua_State *L, lua_State *sL, uint8_t vt, union value *v) { + switch(vt) { + case VALUETYPE_NUMBER: + lua_pushnumber(L, v->n); + break; + case VALUETYPE_STRING: { + size_t sz = 0; + const char *str = lua_tolstring(sL, v->string, &sz); + lua_pushlstring(L, str, sz); + break; + } + case VALUETYPE_BOOLEAN: + lua_pushboolean(L, v->boolean); + break; + case VALUETYPE_TABLE: + lua_pushlightuserdata(L, v->tbl); + break; + default: + lua_pushnil(L); + break; + } +} + +static struct node * +lookup_key(struct table *tbl, uint32_t keyhash, int key, int keytype, const char *str, size_t sz) { + if (tbl->sizehash == 0) + return NULL; + struct node *n = &tbl->hash[keyhash % tbl->sizehash]; + if (keyhash != n->keyhash && n->nocolliding) + return NULL; + for (;;) { + if (keyhash == n->keyhash) { + if (n->keytype == KEYTYPE_INTEGER) { + if (keytype == KEYTYPE_INTEGER && n->key == key) { + return n; + } + } else { + // n->keytype == KEYTYPE_STRING + if (keytype == KEYTYPE_STRING) { + size_t sz2 = 0; + const char * str2 = lua_tolstring(tbl->L, n->key, &sz2); + if (sz == sz2 && memcmp(str,str2,sz) == 0) { + return n; + } + } + } + } + if (n->next < 0) { + return NULL; + } + n = &tbl->hash[n->next]; + } +} + +static int +lindexconf(lua_State *L) { + struct table *tbl = get_table(L,1); + int kt = lua_type(L,2); + uint32_t keyhash; + int key = 0; + int keytype; + size_t sz = 0; + const char * str = NULL; + if (kt == LUA_TNUMBER) { + lua_Number k = lua_tonumber(L, 2); + key = (int)k; + if ((lua_Number)key != k) { + return luaL_error(L, "Invalid key %f", k); + } + if (key > 0 && key <= tbl->sizearray) { + --key; + pushvalue(L, tbl->L, tbl->arraytype[key], &tbl->array[key]); + return 1; + } + keytype = KEYTYPE_INTEGER; + keyhash = (uint32_t)key; + } else { + str = luaL_checklstring(L, 2, &sz); + keyhash = calchash(str, sz); + keytype = KEYTYPE_STRING; + } + + struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz); + if (n) { + pushvalue(L, tbl->L, n->valuetype, &n->v); + return 1; + } else { + return 0; + } +} + +static void +pushkey(lua_State *L, lua_State *sL, struct node *n) { + if (n->keytype == KEYTYPE_INTEGER) { + lua_pushinteger(L, n->key); + } else { + size_t sz = 0; + const char * str = lua_tolstring(sL, n->key, &sz); + lua_pushlstring(L, str, sz); + } +} + +static int +pushfirsthash(lua_State *L, struct table * tbl) { + if (tbl->sizehash) { + pushkey(L, tbl->L, &tbl->hash[0]); + return 1; + } else { + return 0; + } +} + +static int +lnextkey(lua_State *L) { + struct table *tbl = get_table(L,1); + if (lua_isnoneornil(L,2)) { + if (tbl->sizearray > 0) { + int i; + for (i=0;isizearray;i++) { + if (tbl->arraytype[i] != VALUETYPE_NIL) { + lua_pushinteger(L, i+1); + return 1; + } + } + } + return pushfirsthash(L, tbl); + } + int kt = lua_type(L,2); + uint32_t keyhash; + int key = 0; + int keytype; + size_t sz=0; + const char *str = NULL; + int sizearray = tbl->sizearray; + if (kt == LUA_TNUMBER) { + lua_Number k = lua_tonumber(L,2); + key = (int)k; + if ((lua_Number)key != k) { + return 0; + } + if (key > 0 && key <= sizearray) { + int i; + for (i=key;iarraytype[i] != VALUETYPE_NIL) { + lua_pushinteger(L, i+1); + return 1; + } + } + return pushfirsthash(L, tbl); + } + keyhash = (uint32_t)key; + keytype = KEYTYPE_INTEGER; + } else { + str = luaL_checklstring(L, 2, &sz); + keyhash = calchash(str, sz); + keytype = KEYTYPE_STRING; + } + + struct node *n = lookup_key(tbl, keyhash, key, keytype, str, sz); + if (n) { + ++n; + int index = n-tbl->hash; + if (index == tbl->sizehash) { + return 0; + } + pushkey(L, tbl->L, n); + return 1; + } else { + return 0; + } +} + +static int +llen(lua_State *L) { + struct table *tbl = get_table(L,1); + lua_pushinteger(L, tbl->sizearray); + return 1; +} + +static int +lhashlen(lua_State *L) { + struct table *tbl = get_table(L,1); + lua_pushinteger(L, tbl->sizehash); + return 1; +} + +static int +releaseobj(lua_State *L) { + struct ctrl *c = lua_touserdata(L, 1); + struct table *tbl = c->root; + struct state *s = lua_touserdata(tbl->L, 1); + __sync_fetch_and_sub(&s->ref, 1); + c->root = NULL; + c->update = NULL; + + return 0; +} + +static int +lboxconf(lua_State *L) { + struct table * tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + __sync_fetch_and_add(&s->ref, 1); + + struct ctrl * c = lua_newuserdata(L, sizeof(*c)); + c->root = tbl; + c->update = NULL; + if (luaL_newmetatable(L, "confctrl")) { + lua_pushcfunction(L, releaseobj); + lua_setfield(L, -2, "__gc"); + } + lua_setmetatable(L, -2); + + return 1; +} + +static int +lmarkdirty(lua_State *L) { + struct table *tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + s->dirty = 1; + return 0; +} + +static int +lisdirty(lua_State *L) { + struct table *tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + int d = s->dirty; + lua_pushboolean(L, d); + + return 1; +} + +static int +lgetref(lua_State *L) { + struct table *tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + lua_pushinteger(L , s->ref); + + return 1; +} + +static int +lincref(lua_State *L) { + struct table *tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + int ref = __sync_add_and_fetch(&s->ref, 1); + lua_pushinteger(L , ref); + + return 1; +} + +static int +ldecref(lua_State *L) { + struct table *tbl = get_table(L,1); + struct state * s = lua_touserdata(tbl->L, 1); + int ref = __sync_sub_and_fetch(&s->ref, 1); + lua_pushinteger(L , ref); + + return 1; +} + +static int +lneedupdate(lua_State *L) { + struct ctrl * c = lua_touserdata(L, 1); + if (c->update) { + lua_pushlightuserdata(L, c->update); + lua_getuservalue(L, 1); + return 2; + } + return 0; +} + +static int +lupdate(lua_State *L) { + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checktype(L, 2, LUA_TLIGHTUSERDATA); + luaL_checktype(L, 3, LUA_TTABLE); + struct ctrl * c= lua_touserdata(L, 1); + struct table *n = lua_touserdata(L, 2); + if (c->update) { + return luaL_error(L, "can't update more than once"); + } + if (c->root == n) { + return luaL_error(L, "You should update a new object"); + } + lua_settop(L, 3); + lua_setuservalue(L, 1); + c->update = n; + + return 0; +} + +int +luaopen_sharedata_core(lua_State *L) { + luaL_Reg l[] = { + // used by host + { "new", lnewconf }, + { "delete", ldeleteconf }, + { "markdirty", lmarkdirty }, + { "getref", lgetref }, + { "incref", lincref }, + { "decref", ldecref }, + + // used by client + { "box", lboxconf }, + { "index", lindexconf }, + { "nextkey", lnextkey }, + { "len", llen }, + { "hashlen", lhashlen }, + { "isdirty", lisdirty }, + { "needupdate", lneedupdate }, + { "update", lupdate }, + { NULL, NULL }, + }; + luaL_checkversion(L); + luaL_newlib(L, l); + + return 1; +} diff --git a/lualib/sharedata.lua b/lualib/sharedata.lua new file mode 100644 index 00000000..edbf61c5 --- /dev/null +++ b/lualib/sharedata.lua @@ -0,0 +1,37 @@ +local skynet = require "skynet" +local sd = require "sharedata.corelib" + +local service + +skynet.init(function() + service = skynet.uniqueservice "sharedatad" +end) + +local sharedata = {} + +local function monitor(name, obj, cobj) + local newobj = skynet.call(service, "lua", "monitor", name, cobj) + sd.update(obj, newobj) +end + +function sharedata.query(name) + local obj = skynet.call(service, "lua", "query", name) + local r = sd.box(obj) + skynet.send(service, "lua", "confirm" , obj) + skynet.fork(monitor,name, r, obj) + return r +end + +function sharedata.new(name, v) + skynet.call(service, "lua", "new", name, v) +end + +function sharedata.update(name, v) + skynet.call(service, "lua", "update", name, v) +end + +function sharedata.delete(name) + skynet.call(service, "lua", "delete", name) +end + +return sharedata diff --git a/lualib/sharedata/corelib.lua b/lualib/sharedata/corelib.lua new file mode 100644 index 00000000..d9a5488a --- /dev/null +++ b/lualib/sharedata/corelib.lua @@ -0,0 +1,134 @@ +local core = require "sharedata.core" +local type = type +local next = next +local rawget = rawget + +local conf = {} + +conf.host = { + new = core.new, + delete = core.delete, + getref = core.getref, + markdirty = core.markdirty, + incref = core.incref, + decref = core.decref, +} + +local meta = {} + +local isdirty = core.isdirty +local index = core.index +local needupdate = core.needupdate +local len = core.len + +local function findroot(self) + while self.__parent do + self = self.__parent + end + return self +end + +local function update(root, cobj, gcobj) + root.__obj = cobj + root.__gcobj = gcobj + -- don't use pairs + for k,v in next, root do + if type(v)=="table" and k~="__parent" then + local pointer = index(cobj, k) + if type(pointer) == "userdata" then + update(v, pointer, gcobj) + else + root[k] = nil + end + end + end +end + +local function genkey(self) + local key = tostring(self.__key) + while self.__parent do + self = self.__parent + key = self.__key .. "." .. key + end + return key +end + +function meta:__index(key) + local obj = self.__obj + if isdirty(obj) then + local newobj, newtbl = needupdate(self.__gcobj) + if newobj then + local newgcobj = newtbl.__gcobj + -- todo: update + local root = findroot(self) + update(root, newobj, newgcobj) + if obj == self.__obj then + error ("The key [" .. genkey(self) .. "] doesn't exist after update") + end + end + end + local v = index(self.__obj, key) + if type(v) == "userdata" then + local r = setmetatable({ + __obj = v, + __gcobj = self.__gcobj, + __parent = self, + __key = key, + }, meta) + self[key] = r + return r + else + return v + end +end + +function meta:__len() + return len(self.__obj) +end + +local function conf_ipairs(self, index) + local obj = self.__obj + index = index + 1 + local value = rawget(self, index) + if value then + return index, value + end + local sz = len(obj) + if sz < index then + return + end + return index, self[index] +end + +function meta:__ipairs() + return conf_ipairs, self, 0 +end + +function meta:__pairs() + return conf.next, self, nil +end + +function conf.next(obj, key) + local nextkey = core.nextkey(obj.__obj, key) + if nextkey then + return nextkey, obj[nextkey] + end +end + +function conf.box(obj) + local gcobj = core.box(obj) + return setmetatable({ + __parent = false, + __obj = obj, + __gcobj = gcobj, + __key = "", + } , meta) +end + +function conf.update(self, pointer) + local cobj = self.__obj + assert(isdirty(cobj), "Obly dirty object can be update") + core.update(self.__gcobj, pointer, { __gcobj = core.box(pointer) }) +end + +return conf \ No newline at end of file diff --git a/service/sharedatad.lua b/service/sharedatad.lua new file mode 100644 index 00000000..7ce24b89 --- /dev/null +++ b/service/sharedatad.lua @@ -0,0 +1,121 @@ +local skynet = require "skynet" +local sharedata = require "sharedata.corelib" +local table = table + +local NORET = {} +local pool = {} +local objmap = {} + +local function newobj(name, tbl) + assert(pool[name] == nil) + local cobj = sharedata.host.new(tbl) + sharedata.host.incref(cobj) + local v = { value = tbl , obj = cobj, watch = {} } + objmap[cobj] = v + pool[name] = v +end + +local function collectobj() + while true do + skynet.sleep(600 * 100) -- sleep 10 min + collectgarbage() + for obj, v in pairs(objmap) do + if v == true then + if sharedata.host.getref(obj) <= 0 then + objmap[obj] = nil + print("collect", obj) + sharedata.host.delete(obj) + end + end + end + end +end + +local CMD = {} + +function CMD.new(name, t) + local dt = type(t) + local value + if dt == "table" then + value = t + elseif dt == "string" then + value = {} + local f = load(t, "=" .. name, "t", env) + f() + elseif dt == "nil" then + value = {} + else + error ("Unknown data type " .. dt) + end + newobj(name, value) +end + +function CMD.delete(name) + local v = assert(pool[name]) + pool[name] = nil + assert(objmap[v.obj]) + objmap[v.obj] = true + sharedata.host.decref(v.obj) +end + +function CMD.query(name) + local v = assert(pool[name]) + local obj = v.obj + sharedata.host.incref(obj) + return v.obj +end + +function CMD.confirm(cobj) + if objmap[cobj] then + sharedata.host.decref(cobj) + end + return NORET +end + +function CMD.update(name, t) + local v = pool[name] + local watch, oldcobj + if v then + watch = v.watch + oldcobj = v.obj + CMD.delete(name) + end + CMD.new(name, t) + local newobj = pool[name].obj + if watch then + sharedata.host.markdirty(oldcobj) + for _,v in ipairs(watch) do + local session = v[1] + local address = v[2] + skynet.redirect(address, 0, "response", session, skynet.pack(newobj)) + end + end +end + +function CMD.monitor(session, address, name, obj) + local v = assert(pool[name]) + if obj ~= v.obj then + return v.obj + end + + table.insert(v.watch, { session, address }) + + return NORET +end + +skynet.start(function() + skynet.fork(collectobj) + skynet.dispatch("lua", function (session, source ,cmd, ...) + local f = assert(CMD[cmd]) + local r + if cmd == "monitor" then + r = f(session, source, ...) + else + r = f(...) + end + if r ~= NORET then + skynet.ret(skynet.pack(r)) + end + end) +end) +