mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-25 04:33:05 +00:00
add stm object support. (STM : Software transactional memory)
This commit is contained in:
244
lualib-src/lua-stm.c
Normal file
244
lualib-src/lua-stm.c
Normal file
@@ -0,0 +1,244 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "rwlock.h"
|
||||
#include "skynet_malloc.h"
|
||||
|
||||
struct stm_object {
|
||||
struct rwlock lock;
|
||||
int reference;
|
||||
struct stm_copy * copy;
|
||||
};
|
||||
|
||||
struct stm_copy {
|
||||
int reference;
|
||||
uint32_t sz;
|
||||
void * msg;
|
||||
};
|
||||
|
||||
// msg should alloc by skynet_malloc
|
||||
static struct stm_copy *
|
||||
stm_newcopy(void * msg, int32_t sz) {
|
||||
struct stm_copy * copy = skynet_malloc(sizeof(*copy));
|
||||
copy->reference = 1;
|
||||
copy->sz = sz;
|
||||
copy->msg = msg;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static struct stm_object *
|
||||
stm_new(void * msg, int32_t sz) {
|
||||
struct stm_object * obj = skynet_malloc(sizeof(*obj));
|
||||
rwlock_init(&obj->lock);
|
||||
obj->reference = 1;
|
||||
obj->copy = stm_newcopy(msg, sz);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void
|
||||
stm_releasecopy(struct stm_copy *copy) {
|
||||
if (copy == NULL)
|
||||
return;
|
||||
if (__sync_sub_and_fetch(©->reference, 1) == 0) {
|
||||
skynet_free(copy->msg);
|
||||
skynet_free(copy);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stm_release(struct stm_object *obj) {
|
||||
assert(obj->copy);
|
||||
rwlock_wlock(&obj->lock);
|
||||
// writer release the stm object, so release the last copy .
|
||||
stm_releasecopy(obj->copy);
|
||||
obj->copy = NULL;
|
||||
if (--obj->reference > 0) {
|
||||
// stm object grab by readers, reset the copy to NULL.
|
||||
rwlock_wunlock(&obj->lock);
|
||||
return;
|
||||
}
|
||||
// no one grab the stm object, no need to unlock wlock.
|
||||
skynet_free(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
stm_releasereader(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
if (__sync_sub_and_fetch(&obj->reference,1) == 0) {
|
||||
// last reader, no writer. so no need to unlock
|
||||
assert(obj->copy == NULL);
|
||||
skynet_free(obj);
|
||||
return;
|
||||
}
|
||||
rwlock_runlock(&obj->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
stm_grab(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
int ref = __sync_fetch_and_add(&obj->reference,1);
|
||||
rwlock_runlock(&obj->lock);
|
||||
assert(ref > 0);
|
||||
}
|
||||
|
||||
static struct stm_copy *
|
||||
stm_copy(struct stm_object *obj) {
|
||||
rwlock_rlock(&obj->lock);
|
||||
struct stm_copy * ret = obj->copy;
|
||||
if (ret) {
|
||||
int ref = __sync_fetch_and_add(&ret->reference,1);
|
||||
assert(ref > 0);
|
||||
}
|
||||
rwlock_runlock(&obj->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
stm_update(struct stm_object *obj, void *msg, int32_t sz) {
|
||||
struct stm_copy *copy = stm_newcopy(msg, sz);
|
||||
rwlock_wlock(&obj->lock);
|
||||
struct stm_copy *oldcopy = obj->copy;
|
||||
obj->copy = copy;
|
||||
rwlock_wunlock(&obj->lock);
|
||||
|
||||
stm_releasecopy(oldcopy);
|
||||
}
|
||||
|
||||
// lua binding
|
||||
|
||||
struct boxstm {
|
||||
struct stm_object * obj;
|
||||
};
|
||||
|
||||
static int
|
||||
lcopy(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
stm_grab(box->obj);
|
||||
lua_pushlightuserdata(L, box->obj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
lnewwriter(lua_State *L) {
|
||||
void * msg = lua_touserdata(L, 1);
|
||||
uint32_t sz = luaL_checkunsigned(L, 2);
|
||||
struct boxstm * box = lua_newuserdata(L, sizeof(*box));
|
||||
box->obj = stm_new(msg,sz);
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeletewriter(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
stm_release(box->obj);
|
||||
box->obj = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lupdate(lua_State *L) {
|
||||
struct boxstm * box = lua_touserdata(L, 1);
|
||||
void * msg = lua_touserdata(L, 2);
|
||||
uint32_t sz = luaL_checkunsigned(L, 3);
|
||||
stm_update(box->obj, msg, sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct boxreader {
|
||||
struct stm_object *obj;
|
||||
struct stm_copy *lastcopy;
|
||||
};
|
||||
|
||||
static int
|
||||
lnewreader(lua_State *L) {
|
||||
struct boxreader * box = lua_newuserdata(L, sizeof(*box));
|
||||
box->obj = lua_touserdata(L, 1);
|
||||
box->lastcopy = NULL;
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ldeletereader(lua_State *L) {
|
||||
struct boxreader * box = lua_touserdata(L, 1);
|
||||
stm_releasereader(box->obj);
|
||||
box->obj = NULL;
|
||||
stm_releasecopy(box->lastcopy);
|
||||
box->lastcopy = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lread(lua_State *L) {
|
||||
struct boxreader * box = lua_touserdata(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
struct stm_copy * copy = stm_copy(box->obj);
|
||||
if (copy == box->lastcopy) {
|
||||
// not update
|
||||
stm_releasecopy(copy);
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
stm_releasecopy(box->lastcopy);
|
||||
box->lastcopy = copy;
|
||||
if (copy) {
|
||||
lua_settop(L, 2);
|
||||
lua_pushlightuserdata(L, copy->msg);
|
||||
lua_pushunsigned(L, copy->sz);
|
||||
lua_call(L, 2, LUA_MULTRET);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_replace(L, 1);
|
||||
return lua_gettop(L);
|
||||
} else {
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
luaopen_stm(lua_State *L) {
|
||||
luaL_checkversion(L);
|
||||
lua_createtable(L, 0, 3);
|
||||
|
||||
lua_pushcfunction(L, lcopy);
|
||||
lua_setfield(L, -2, "copy");
|
||||
|
||||
luaL_Reg writer[] = {
|
||||
{ "new", lnewwriter },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_pushcfunction(L, ldeletewriter),
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, lupdate),
|
||||
lua_setfield(L, -2, "__call");
|
||||
luaL_setfuncs(L, writer, 1);
|
||||
|
||||
luaL_Reg reader[] = {
|
||||
{ "newcopy", lnewreader },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_pushcfunction(L, ldeletereader),
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pushcfunction(L, lread),
|
||||
lua_setfield(L, -2, "__call");
|
||||
luaL_setfuncs(L, reader, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user