lua-debugchannel.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #define LUA_LIB
  2. // only for debug use
  3. #include <lua.h>
  4. #include <lauxlib.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include "spinlock.h"
  9. #define METANAME "debugchannel"
  10. struct command {
  11. struct command * next;
  12. size_t sz;
  13. };
  14. struct channel {
  15. struct spinlock lock;
  16. int ref;
  17. struct command * head;
  18. struct command * tail;
  19. };
  20. static struct channel *
  21. channel_new() {
  22. struct channel * c = malloc(sizeof(*c));
  23. memset(c, 0 , sizeof(*c));
  24. c->ref = 1;
  25. SPIN_INIT(c)
  26. return c;
  27. }
  28. static struct channel *
  29. channel_connect(struct channel *c) {
  30. struct channel * ret = NULL;
  31. SPIN_LOCK(c)
  32. if (c->ref == 1) {
  33. ++c->ref;
  34. ret = c;
  35. }
  36. SPIN_UNLOCK(c)
  37. return ret;
  38. }
  39. static struct channel *
  40. channel_release(struct channel *c) {
  41. SPIN_LOCK(c)
  42. --c->ref;
  43. if (c->ref > 0) {
  44. SPIN_UNLOCK(c)
  45. return c;
  46. }
  47. // never unlock while reference is 0
  48. struct command * p = c->head;
  49. c->head = NULL;
  50. c->tail = NULL;
  51. while(p) {
  52. struct command *next = p->next;
  53. free(p);
  54. p = next;
  55. }
  56. SPIN_UNLOCK(c)
  57. SPIN_DESTROY(c)
  58. free(c);
  59. return NULL;
  60. }
  61. // call free after channel_read
  62. static struct command *
  63. channel_read(struct channel *c, double timeout) {
  64. struct command * ret = NULL;
  65. SPIN_LOCK(c)
  66. if (c->head == NULL) {
  67. SPIN_UNLOCK(c)
  68. int ti = (int)(timeout * 100000);
  69. usleep(ti);
  70. return NULL;
  71. }
  72. ret = c->head;
  73. c->head = ret->next;
  74. if (c->head == NULL) {
  75. c->tail = NULL;
  76. }
  77. SPIN_UNLOCK(c)
  78. return ret;
  79. }
  80. static void
  81. channel_write(struct channel *c, const char * s, size_t sz) {
  82. struct command * cmd = malloc(sizeof(*cmd)+ sz);
  83. cmd->sz = sz;
  84. cmd->next = NULL;
  85. memcpy(cmd+1, s, sz);
  86. SPIN_LOCK(c)
  87. if (c->tail == NULL) {
  88. c->head = c->tail = cmd;
  89. } else {
  90. c->tail->next = cmd;
  91. c->tail = cmd;
  92. }
  93. SPIN_UNLOCK(c)
  94. }
  95. struct channel_box {
  96. struct channel *c;
  97. };
  98. static int
  99. lread(lua_State *L) {
  100. struct channel_box *cb = luaL_checkudata(L,1, METANAME);
  101. double ti = luaL_optnumber(L, 2, 0);
  102. struct command * c = channel_read(cb->c, ti);
  103. if (c == NULL)
  104. return 0;
  105. lua_pushlstring(L, (const char *)(c+1), c->sz);
  106. free(c);
  107. return 1;
  108. }
  109. static int
  110. lwrite(lua_State *L) {
  111. struct channel_box *cb = luaL_checkudata(L,1, METANAME);
  112. size_t sz;
  113. const char * str = luaL_checklstring(L, 2, &sz);
  114. channel_write(cb->c, str, sz);
  115. return 0;
  116. }
  117. static int
  118. lrelease(lua_State *L) {
  119. struct channel_box *cb = lua_touserdata(L, 1);
  120. if (cb) {
  121. if (channel_release(cb->c) == NULL) {
  122. cb->c = NULL;
  123. }
  124. }
  125. return 0;
  126. }
  127. static struct channel *
  128. new_channel(lua_State *L, struct channel *c) {
  129. if (c == NULL) {
  130. c = channel_new();
  131. } else {
  132. c = channel_connect(c);
  133. }
  134. if (c == NULL) {
  135. luaL_error(L, "new channel failed");
  136. // never go here
  137. }
  138. struct channel_box * cb = lua_newuserdatauv(L, sizeof(*cb), 0);
  139. cb->c = c;
  140. if (luaL_newmetatable(L, METANAME)) {
  141. luaL_Reg l[]={
  142. { "read", lread },
  143. { "write", lwrite },
  144. { NULL, NULL },
  145. };
  146. luaL_newlib(L,l);
  147. lua_setfield(L, -2, "__index");
  148. lua_pushcfunction(L, lrelease);
  149. lua_setfield(L, -2, "__gc");
  150. }
  151. lua_setmetatable(L, -2);
  152. return c;
  153. }
  154. static int
  155. lcreate(lua_State *L) {
  156. struct channel *c = new_channel(L, NULL);
  157. lua_pushlightuserdata(L, c);
  158. return 2;
  159. }
  160. static int
  161. lconnect(lua_State *L) {
  162. struct channel *c = lua_touserdata(L, 1);
  163. if (c == NULL)
  164. return luaL_error(L, "Invalid channel pointer");
  165. new_channel(L, c);
  166. return 1;
  167. }
  168. static const int HOOKKEY = 0;
  169. /*
  170. ** Auxiliary function used by several library functions: check for
  171. ** an optional thread as function's first argument and set 'arg' with
  172. ** 1 if this argument is present (so that functions can skip it to
  173. ** access their other arguments)
  174. */
  175. static lua_State *getthread (lua_State *L, int *arg) {
  176. if (lua_isthread(L, 1)) {
  177. *arg = 1;
  178. return lua_tothread(L, 1);
  179. }
  180. else {
  181. *arg = 0;
  182. return L; /* function will operate over current thread */
  183. }
  184. }
  185. /*
  186. ** Call hook function registered at hook table for the current
  187. ** thread (if there is one)
  188. */
  189. static void hookf (lua_State *L, lua_Debug *ar) {
  190. static const char *const hooknames[] =
  191. {"call", "return", "line", "count", "tail call"};
  192. lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
  193. lua_pushthread(L);
  194. if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
  195. lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
  196. if (ar->currentline >= 0)
  197. lua_pushinteger(L, ar->currentline); /* push current line */
  198. else lua_pushnil(L);
  199. lua_call(L, 2, 1); /* call hook function */
  200. int yield = lua_toboolean(L, -1);
  201. lua_pop(L,1);
  202. if (yield) {
  203. lua_yield(L, 0);
  204. }
  205. }
  206. }
  207. /*
  208. ** Convert a string mask (for 'sethook') into a bit mask
  209. */
  210. static int makemask (const char *smask, int count) {
  211. int mask = 0;
  212. if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
  213. if (strchr(smask, 'r')) mask |= LUA_MASKRET;
  214. if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
  215. if (count > 0) mask |= LUA_MASKCOUNT;
  216. return mask;
  217. }
  218. static int db_sethook (lua_State *L) {
  219. int arg, mask, count;
  220. lua_Hook func;
  221. lua_State *L1 = getthread(L, &arg);
  222. if (lua_isnoneornil(L, arg+1)) { /* no hook? */
  223. lua_settop(L, arg+1);
  224. func = NULL; mask = 0; count = 0; /* turn off hooks */
  225. }
  226. else {
  227. const char *smask = luaL_checkstring(L, arg+2);
  228. luaL_checktype(L, arg+1, LUA_TFUNCTION);
  229. count = (int)luaL_optinteger(L, arg + 3, 0);
  230. func = hookf; mask = makemask(smask, count);
  231. }
  232. if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) {
  233. lua_createtable(L, 0, 2); /* create a hook table */
  234. lua_pushvalue(L, -1);
  235. lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */
  236. lua_pushstring(L, "k");
  237. lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
  238. lua_pushvalue(L, -1);
  239. lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
  240. }
  241. lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
  242. lua_pushvalue(L, arg + 1); /* value (hook function) */
  243. lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
  244. lua_sethook(L1, func, mask, count);
  245. return 0;
  246. }
  247. LUAMOD_API int
  248. luaopen_skynet_debugchannel(lua_State *L) {
  249. luaL_Reg l[] = {
  250. { "create", lcreate }, // for write
  251. { "connect", lconnect }, // for read
  252. { "release", lrelease },
  253. { "sethook", db_sethook },
  254. { NULL, NULL },
  255. };
  256. luaL_checkversion(L);
  257. luaL_newlib(L,l);
  258. return 1;
  259. }