service_snlua.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #include "skynet.h"
  2. #include "atomic.h"
  3. #include <lua.h>
  4. #include <lualib.h>
  5. #include <lauxlib.h>
  6. #include <assert.h>
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <time.h>
  11. #if defined(__APPLE__)
  12. #include <mach/task.h>
  13. #include <mach/mach.h>
  14. #endif
  15. #define NANOSEC 1000000000
  16. #define MICROSEC 1000000
  17. // #define DEBUG_LOG
  18. #define MEMORY_WARNING_REPORT (1024 * 1024 * 32)
  19. struct snlua {
  20. lua_State * L;
  21. struct skynet_context * ctx;
  22. size_t mem;
  23. size_t mem_report;
  24. size_t mem_limit;
  25. lua_State * activeL;
  26. ATOM_INT trap;
  27. };
  28. // LUA_CACHELIB may defined in patched lua for shared proto
  29. #ifdef LUA_CACHELIB
  30. #define codecache luaopen_cache
  31. #else
  32. static int
  33. cleardummy(lua_State *L) {
  34. return 0;
  35. }
  36. static int
  37. codecache(lua_State *L) {
  38. luaL_Reg l[] = {
  39. { "clear", cleardummy },
  40. { "mode", cleardummy },
  41. { NULL, NULL },
  42. };
  43. luaL_newlib(L,l);
  44. lua_getglobal(L, "loadfile");
  45. lua_setfield(L, -2, "loadfile");
  46. return 1;
  47. }
  48. #endif
  49. static void
  50. signal_hook(lua_State *L, lua_Debug *ar) {
  51. void *ud = NULL;
  52. lua_getallocf(L, &ud);
  53. struct snlua *l = (struct snlua *)ud;
  54. lua_sethook (L, NULL, 0, 0);
  55. if (ATOM_LOAD(&l->trap)) {
  56. ATOM_STORE(&l->trap , 0);
  57. luaL_error(L, "signal 0");
  58. }
  59. }
  60. static void
  61. switchL(lua_State *L, struct snlua *l) {
  62. l->activeL = L;
  63. if (ATOM_LOAD(&l->trap)) {
  64. lua_sethook(L, signal_hook, LUA_MASKCOUNT, 1);
  65. }
  66. }
  67. static int
  68. lua_resumeX(lua_State *L, lua_State *from, int nargs, int *nresults) {
  69. void *ud = NULL;
  70. lua_getallocf(L, &ud);
  71. struct snlua *l = (struct snlua *)ud;
  72. switchL(L, l);
  73. int err = lua_resume(L, from, nargs, nresults);
  74. if (ATOM_LOAD(&l->trap)) {
  75. // wait for lua_sethook. (l->trap == -1)
  76. while (ATOM_LOAD(&l->trap) >= 0) ;
  77. }
  78. switchL(from, l);
  79. return err;
  80. }
  81. static double
  82. get_time() {
  83. #if !defined(__APPLE__)
  84. struct timespec ti;
  85. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti);
  86. int sec = ti.tv_sec & 0xffff;
  87. int nsec = ti.tv_nsec;
  88. return (double)sec + (double)nsec / NANOSEC;
  89. #else
  90. struct task_thread_times_info aTaskInfo;
  91. mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT;
  92. if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount)) {
  93. return 0;
  94. }
  95. int sec = aTaskInfo.user_time.seconds & 0xffff;
  96. int msec = aTaskInfo.user_time.microseconds;
  97. return (double)sec + (double)msec / MICROSEC;
  98. #endif
  99. }
  100. static inline double
  101. diff_time(double start) {
  102. double now = get_time();
  103. if (now < start) {
  104. return now + 0x10000 - start;
  105. } else {
  106. return now - start;
  107. }
  108. }
  109. // coroutine lib, add profile
  110. /*
  111. ** Resumes a coroutine. Returns the number of results for non-error
  112. ** cases or -1 for errors.
  113. */
  114. static int auxresume (lua_State *L, lua_State *co, int narg) {
  115. int status, nres;
  116. if (!lua_checkstack(co, narg)) {
  117. lua_pushliteral(L, "too many arguments to resume");
  118. return -1; /* error flag */
  119. }
  120. lua_xmove(L, co, narg);
  121. status = lua_resumeX(co, L, narg, &nres);
  122. if (status == LUA_OK || status == LUA_YIELD) {
  123. if (!lua_checkstack(L, nres + 1)) {
  124. lua_pop(co, nres); /* remove results anyway */
  125. lua_pushliteral(L, "too many results to resume");
  126. return -1; /* error flag */
  127. }
  128. lua_xmove(co, L, nres); /* move yielded values */
  129. return nres;
  130. }
  131. else {
  132. lua_xmove(co, L, 1); /* move error message */
  133. return -1; /* error flag */
  134. }
  135. }
  136. static int
  137. timing_enable(lua_State *L, int co_index, lua_Number *start_time) {
  138. lua_pushvalue(L, co_index);
  139. lua_rawget(L, lua_upvalueindex(1));
  140. if (lua_isnil(L, -1)) { // check total time
  141. lua_pop(L, 1);
  142. return 0;
  143. }
  144. *start_time = lua_tonumber(L, -1);
  145. lua_pop(L,1);
  146. return 1;
  147. }
  148. static double
  149. timing_total(lua_State *L, int co_index) {
  150. lua_pushvalue(L, co_index);
  151. lua_rawget(L, lua_upvalueindex(2));
  152. double total_time = lua_tonumber(L, -1);
  153. lua_pop(L,1);
  154. return total_time;
  155. }
  156. static int
  157. timing_resume(lua_State *L, int co_index, int n) {
  158. lua_State *co = lua_tothread(L, co_index);
  159. lua_Number start_time = 0;
  160. if (timing_enable(L, co_index, &start_time)) {
  161. start_time = get_time();
  162. #ifdef DEBUG_LOG
  163. fprintf(stderr, "PROFILE [%p] resume %lf\n", co, ti);
  164. #endif
  165. lua_pushvalue(L, co_index);
  166. lua_pushnumber(L, start_time);
  167. lua_rawset(L, lua_upvalueindex(1)); // set start time
  168. }
  169. int r = auxresume(L, co, n);
  170. if (timing_enable(L, co_index, &start_time)) {
  171. double total_time = timing_total(L, co_index);
  172. double diff = diff_time(start_time);
  173. total_time += diff;
  174. #ifdef DEBUG_LOG
  175. fprintf(stderr, "PROFILE [%p] yield (%lf/%lf)\n", co, diff, total_time);
  176. #endif
  177. lua_pushvalue(L, co_index);
  178. lua_pushnumber(L, total_time);
  179. lua_rawset(L, lua_upvalueindex(2));
  180. }
  181. return r;
  182. }
  183. static int luaB_coresume (lua_State *L) {
  184. luaL_checktype(L, 1, LUA_TTHREAD);
  185. int r = timing_resume(L, 1, lua_gettop(L) - 1);
  186. if (r < 0) {
  187. lua_pushboolean(L, 0);
  188. lua_insert(L, -2);
  189. return 2; /* return false + error message */
  190. }
  191. else {
  192. lua_pushboolean(L, 1);
  193. lua_insert(L, -(r + 1));
  194. return r + 1; /* return true + 'resume' returns */
  195. }
  196. }
  197. static int luaB_auxwrap (lua_State *L) {
  198. lua_State *co = lua_tothread(L, lua_upvalueindex(3));
  199. int r = timing_resume(L, lua_upvalueindex(3), lua_gettop(L));
  200. if (r < 0) {
  201. int stat = lua_status(co);
  202. if (stat != LUA_OK && stat != LUA_YIELD)
  203. lua_closethread(co, L); /* close variables in case of errors */
  204. if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
  205. luaL_where(L, 1); /* add extra info, if available */
  206. lua_insert(L, -2);
  207. lua_concat(L, 2);
  208. }
  209. return lua_error(L); /* propagate error */
  210. }
  211. return r;
  212. }
  213. static int luaB_cocreate (lua_State *L) {
  214. lua_State *NL;
  215. luaL_checktype(L, 1, LUA_TFUNCTION);
  216. NL = lua_newthread(L);
  217. lua_pushvalue(L, 1); /* move function to top */
  218. lua_xmove(L, NL, 1); /* move function from L to NL */
  219. return 1;
  220. }
  221. static int luaB_cowrap (lua_State *L) {
  222. lua_pushvalue(L, lua_upvalueindex(1));
  223. lua_pushvalue(L, lua_upvalueindex(2));
  224. luaB_cocreate(L);
  225. lua_pushcclosure(L, luaB_auxwrap, 3);
  226. return 1;
  227. }
  228. // profile lib
  229. static int
  230. lstart(lua_State *L) {
  231. if (lua_gettop(L) != 0) {
  232. lua_settop(L,1);
  233. luaL_checktype(L, 1, LUA_TTHREAD);
  234. } else {
  235. lua_pushthread(L);
  236. }
  237. lua_Number start_time = 0;
  238. if (timing_enable(L, 1, &start_time)) {
  239. return luaL_error(L, "Thread %p start profile more than once", lua_topointer(L, 1));
  240. }
  241. // reset total time
  242. lua_pushvalue(L, 1);
  243. lua_pushnumber(L, 0);
  244. lua_rawset(L, lua_upvalueindex(2));
  245. // set start time
  246. lua_pushvalue(L, 1);
  247. start_time = get_time();
  248. #ifdef DEBUG_LOG
  249. fprintf(stderr, "PROFILE [%p] start\n", L);
  250. #endif
  251. lua_pushnumber(L, start_time);
  252. lua_rawset(L, lua_upvalueindex(1));
  253. return 0;
  254. }
  255. static int
  256. lstop(lua_State *L) {
  257. if (lua_gettop(L) != 0) {
  258. lua_settop(L,1);
  259. luaL_checktype(L, 1, LUA_TTHREAD);
  260. } else {
  261. lua_pushthread(L);
  262. }
  263. lua_Number start_time = 0;
  264. if (!timing_enable(L, 1, &start_time)) {
  265. return luaL_error(L, "Call profile.start() before profile.stop()");
  266. }
  267. double ti = diff_time(start_time);
  268. double total_time = timing_total(L,1);
  269. lua_pushvalue(L, 1); // push coroutine
  270. lua_pushnil(L);
  271. lua_rawset(L, lua_upvalueindex(1));
  272. lua_pushvalue(L, 1); // push coroutine
  273. lua_pushnil(L);
  274. lua_rawset(L, lua_upvalueindex(2));
  275. total_time += ti;
  276. lua_pushnumber(L, total_time);
  277. #ifdef DEBUG_LOG
  278. fprintf(stderr, "PROFILE [%p] stop (%lf/%lf)\n", lua_tothread(L,1), ti, total_time);
  279. #endif
  280. return 1;
  281. }
  282. static int
  283. init_profile(lua_State *L) {
  284. luaL_Reg l[] = {
  285. { "start", lstart },
  286. { "stop", lstop },
  287. { "resume", luaB_coresume },
  288. { "wrap", luaB_cowrap },
  289. { NULL, NULL },
  290. };
  291. luaL_newlibtable(L,l);
  292. lua_newtable(L); // table thread->start time
  293. lua_newtable(L); // table thread->total time
  294. lua_newtable(L); // weak table
  295. lua_pushliteral(L, "kv");
  296. lua_setfield(L, -2, "__mode");
  297. lua_pushvalue(L, -1);
  298. lua_setmetatable(L, -3);
  299. lua_setmetatable(L, -3);
  300. luaL_setfuncs(L,l,2);
  301. return 1;
  302. }
  303. /// end of coroutine
  304. static int
  305. traceback (lua_State *L) {
  306. const char *msg = lua_tostring(L, 1);
  307. if (msg)
  308. luaL_traceback(L, L, msg, 1);
  309. else {
  310. lua_pushliteral(L, "(no error message)");
  311. }
  312. return 1;
  313. }
  314. static void
  315. report_launcher_error(struct skynet_context *ctx) {
  316. // sizeof "ERROR" == 5
  317. skynet_sendname(ctx, 0, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
  318. }
  319. static const char *
  320. optstring(struct skynet_context *ctx, const char *key, const char * str) {
  321. const char * ret = skynet_command(ctx, "GETENV", key);
  322. if (ret == NULL) {
  323. return str;
  324. }
  325. return ret;
  326. }
  327. static int
  328. init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
  329. lua_State *L = l->L;
  330. l->ctx = ctx;
  331. lua_gc(L, LUA_GCSTOP, 0);
  332. lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
  333. lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
  334. luaL_openlibs(L);
  335. luaL_requiref(L, "skynet.profile", init_profile, 0);
  336. int profile_lib = lua_gettop(L);
  337. // replace coroutine.resume / coroutine.wrap
  338. lua_getglobal(L, "coroutine");
  339. lua_getfield(L, profile_lib, "resume");
  340. lua_setfield(L, -2, "resume");
  341. lua_getfield(L, profile_lib, "wrap");
  342. lua_setfield(L, -2, "wrap");
  343. lua_settop(L, profile_lib-1);
  344. lua_pushlightuserdata(L, ctx);
  345. lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context");
  346. luaL_requiref(L, "skynet.codecache", codecache , 0);
  347. lua_pop(L,1);
  348. lua_gc(L, LUA_GCGEN, 0, 0);
  349. const char *path = optstring(ctx, "lua_path","./lualib/?.lua;./lualib/?/init.lua");
  350. lua_pushstring(L, path);
  351. lua_setglobal(L, "LUA_PATH");
  352. const char *cpath = optstring(ctx, "lua_cpath","./luaclib/?.so");
  353. lua_pushstring(L, cpath);
  354. lua_setglobal(L, "LUA_CPATH");
  355. const char *service = optstring(ctx, "luaservice", "./service/?.lua");
  356. lua_pushstring(L, service);
  357. lua_setglobal(L, "LUA_SERVICE");
  358. const char *preload = skynet_command(ctx, "GETENV", "preload");
  359. lua_pushstring(L, preload);
  360. lua_setglobal(L, "LUA_PRELOAD");
  361. lua_pushcfunction(L, traceback);
  362. assert(lua_gettop(L) == 1);
  363. const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
  364. int r = luaL_loadfile(L,loader);
  365. if (r != LUA_OK) {
  366. skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
  367. report_launcher_error(ctx);
  368. return 1;
  369. }
  370. lua_pushlstring(L, args, sz);
  371. r = lua_pcall(L,1,0,1);
  372. if (r != LUA_OK) {
  373. skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1));
  374. report_launcher_error(ctx);
  375. return 1;
  376. }
  377. lua_settop(L,0);
  378. if (lua_getfield(L, LUA_REGISTRYINDEX, "memlimit") == LUA_TNUMBER) {
  379. size_t limit = lua_tointeger(L, -1);
  380. l->mem_limit = limit;
  381. skynet_error(ctx, "Set memory limit to %.2f M", (float)limit / (1024 * 1024));
  382. lua_pushnil(L);
  383. lua_setfield(L, LUA_REGISTRYINDEX, "memlimit");
  384. }
  385. lua_pop(L, 1);
  386. lua_gc(L, LUA_GCRESTART, 0);
  387. return 0;
  388. }
  389. static int
  390. launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
  391. assert(type == 0 && session == 0);
  392. struct snlua *l = ud;
  393. skynet_callback(context, NULL, NULL);
  394. int err = init_cb(l, context, msg, sz);
  395. if (err) {
  396. skynet_command(context, "EXIT", NULL);
  397. }
  398. return 0;
  399. }
  400. int
  401. snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
  402. int sz = strlen(args);
  403. char * tmp = skynet_malloc(sz);
  404. memcpy(tmp, args, sz);
  405. skynet_callback(ctx, l , launch_cb);
  406. const char * self = skynet_command(ctx, "REG", NULL);
  407. uint32_t handle_id = strtoul(self+1, NULL, 16);
  408. // it must be first message
  409. skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
  410. return 0;
  411. }
  412. static void *
  413. lalloc(void * ud, void *ptr, size_t osize, size_t nsize) {
  414. struct snlua *l = ud;
  415. size_t mem = l->mem;
  416. l->mem += nsize;
  417. if (ptr)
  418. l->mem -= osize;
  419. if (l->mem_limit != 0 && l->mem > l->mem_limit) {
  420. if (ptr == NULL || nsize > osize) {
  421. l->mem = mem;
  422. return NULL;
  423. }
  424. }
  425. if (l->mem > l->mem_report) {
  426. l->mem_report *= 2;
  427. skynet_error(l->ctx, "Memory warning %.2f M", (float)l->mem / (1024 * 1024));
  428. }
  429. return skynet_lalloc(ptr, osize, nsize);
  430. }
  431. struct snlua *
  432. snlua_create(void) {
  433. struct snlua * l = skynet_malloc(sizeof(*l));
  434. memset(l,0,sizeof(*l));
  435. l->mem_report = MEMORY_WARNING_REPORT;
  436. l->mem_limit = 0;
  437. l->L = lua_newstate(lalloc, l);
  438. l->activeL = NULL;
  439. ATOM_INIT(&l->trap , 0);
  440. return l;
  441. }
  442. void
  443. snlua_release(struct snlua *l) {
  444. lua_close(l->L);
  445. skynet_free(l);
  446. }
  447. void
  448. snlua_signal(struct snlua *l, int signal) {
  449. skynet_error(l->ctx, "recv a signal %d", signal);
  450. if (signal == 0) {
  451. if (ATOM_LOAD(&l->trap) == 0) {
  452. // only one thread can set trap ( l->trap 0->1 )
  453. if (!ATOM_CAS(&l->trap, 0, 1))
  454. return;
  455. lua_sethook (l->activeL, signal_hook, LUA_MASKCOUNT, 1);
  456. // finish set ( l->trap 1 -> -1 )
  457. ATOM_CAS(&l->trap, 1, -1);
  458. }
  459. } else if (signal == 1) {
  460. skynet_error(l->ctx, "Current Memory %.3fK", (float)l->mem / 1024);
  461. }
  462. }