HomeServer/lualib-src/lua-debugchannel.c

286 lines
6.2 KiB
C
Raw Normal View History

2024-11-20 15:41:09 +08:00
#define LUA_LIB
// only for debug use
#include <lua.h>
#include <lauxlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "spinlock.h"
#define METANAME "debugchannel"
struct command {
struct command * next;
size_t sz;
};
struct channel {
struct spinlock lock;
int ref;
struct command * head;
struct command * tail;
};
static struct channel *
channel_new() {
struct channel * c = malloc(sizeof(*c));
memset(c, 0 , sizeof(*c));
c->ref = 1;
SPIN_INIT(c)
return c;
}
static struct channel *
channel_connect(struct channel *c) {
struct channel * ret = NULL;
SPIN_LOCK(c)
if (c->ref == 1) {
++c->ref;
ret = c;
}
SPIN_UNLOCK(c)
return ret;
}
static struct channel *
channel_release(struct channel *c) {
SPIN_LOCK(c)
--c->ref;
if (c->ref > 0) {
SPIN_UNLOCK(c)
return c;
}
// never unlock while reference is 0
struct command * p = c->head;
c->head = NULL;
c->tail = NULL;
while(p) {
struct command *next = p->next;
free(p);
p = next;
}
SPIN_UNLOCK(c)
SPIN_DESTROY(c)
free(c);
return NULL;
}
// call free after channel_read
static struct command *
channel_read(struct channel *c, double timeout) {
struct command * ret = NULL;
SPIN_LOCK(c)
if (c->head == NULL) {
SPIN_UNLOCK(c)
int ti = (int)(timeout * 100000);
usleep(ti);
return NULL;
}
ret = c->head;
c->head = ret->next;
if (c->head == NULL) {
c->tail = NULL;
}
SPIN_UNLOCK(c)
return ret;
}
static void
channel_write(struct channel *c, const char * s, size_t sz) {
struct command * cmd = malloc(sizeof(*cmd)+ sz);
cmd->sz = sz;
cmd->next = NULL;
memcpy(cmd+1, s, sz);
SPIN_LOCK(c)
if (c->tail == NULL) {
c->head = c->tail = cmd;
} else {
c->tail->next = cmd;
c->tail = cmd;
}
SPIN_UNLOCK(c)
}
struct channel_box {
struct channel *c;
};
static int
lread(lua_State *L) {
struct channel_box *cb = luaL_checkudata(L,1, METANAME);
double ti = luaL_optnumber(L, 2, 0);
struct command * c = channel_read(cb->c, ti);
if (c == NULL)
return 0;
lua_pushlstring(L, (const char *)(c+1), c->sz);
free(c);
return 1;
}
static int
lwrite(lua_State *L) {
struct channel_box *cb = luaL_checkudata(L,1, METANAME);
size_t sz;
const char * str = luaL_checklstring(L, 2, &sz);
channel_write(cb->c, str, sz);
return 0;
}
static int
lrelease(lua_State *L) {
struct channel_box *cb = lua_touserdata(L, 1);
if (cb) {
if (channel_release(cb->c) == NULL) {
cb->c = NULL;
}
}
return 0;
}
static struct channel *
new_channel(lua_State *L, struct channel *c) {
if (c == NULL) {
c = channel_new();
} else {
c = channel_connect(c);
}
if (c == NULL) {
luaL_error(L, "new channel failed");
// never go here
}
struct channel_box * cb = lua_newuserdatauv(L, sizeof(*cb), 0);
cb->c = c;
if (luaL_newmetatable(L, METANAME)) {
luaL_Reg l[]={
{ "read", lread },
{ "write", lwrite },
{ NULL, NULL },
};
luaL_newlib(L,l);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lrelease);
lua_setfield(L, -2, "__gc");
}
lua_setmetatable(L, -2);
return c;
}
static int
lcreate(lua_State *L) {
struct channel *c = new_channel(L, NULL);
lua_pushlightuserdata(L, c);
return 2;
}
static int
lconnect(lua_State *L) {
struct channel *c = lua_touserdata(L, 1);
if (c == NULL)
return luaL_error(L, "Invalid channel pointer");
new_channel(L, c);
return 1;
}
static const int HOOKKEY = 0;
/*
** Auxiliary function used by several library functions: check for
** an optional thread as function's first argument and set 'arg' with
** 1 if this argument is present (so that functions can skip it to
** access their other arguments)
*/
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L; /* function will operate over current thread */
}
}
/*
** Call hook function registered at hook table for the current
** thread (if there is one)
*/
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"};
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
lua_pushthread(L);
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline); /* push current line */
else lua_pushnil(L);
lua_call(L, 2, 1); /* call hook function */
int yield = lua_toboolean(L, -1);
lua_pop(L,1);
if (yield) {
lua_yield(L, 0);
}
}
}
/*
** Convert a string mask (for 'sethook') into a bit mask
*/
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = (int)luaL_optinteger(L, arg + 3, 0);
func = hookf; mask = makemask(smask, count);
}
if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) {
lua_createtable(L, 0, 2); /* create a hook table */
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */
lua_pushstring(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
}
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
lua_pushvalue(L, arg + 1); /* value (hook function) */
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
lua_sethook(L1, func, mask, count);
return 0;
}
LUAMOD_API int
luaopen_skynet_debugchannel(lua_State *L) {
luaL_Reg l[] = {
{ "create", lcreate }, // for write
{ "connect", lconnect }, // for read
{ "release", lrelease },
{ "sethook", db_sethook },
{ NULL, NULL },
};
luaL_checkversion(L);
luaL_newlib(L,l);
return 1;
}