mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-22 02:53:09 +00:00
add sharedata
This commit is contained in:
5
Makefile
5
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
|
||||
|
||||
|
||||
63
examples/share.lua
Normal file
63
examples/share.lua
Normal file
@@ -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
|
||||
789
lualib-src/lua-sharedata.c
Normal file
789
lualib-src/lua-sharedata.c
Normal file
@@ -0,0 +1,789 @@
|
||||
// build: gcc -O2 -Wall --shared -o conf.so luaconf.c
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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;i<sizehash;i++) {
|
||||
if (tbl->hash[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;i<sizearray;i++) {
|
||||
tbl->arraytype[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;i<sizehash;i++) {
|
||||
tbl->hash[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;i<tbl->sizearray;i++) {
|
||||
if (tbl->arraytype[i] == VALUETYPE_TABLE) {
|
||||
delete_tbl(tbl->array[i].tbl);
|
||||
}
|
||||
}
|
||||
for (i=0;i<tbl->sizehash;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;i<tbl->sizearray;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;i<sizearray;i++) {
|
||||
if (tbl->arraytype[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;
|
||||
}
|
||||
37
lualib/sharedata.lua
Normal file
37
lualib/sharedata.lua
Normal file
@@ -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
|
||||
134
lualib/sharedata/corelib.lua
Normal file
134
lualib/sharedata/corelib.lua
Normal file
@@ -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
|
||||
121
service/sharedatad.lua
Normal file
121
service/sharedatad.lua
Normal file
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user