123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- #include "skynet.h"
- #include "atomic.h"
- #include <lua.h>
- #include <lualib.h>
- #include <lauxlib.h>
- #include <assert.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <time.h>
- #if defined(__APPLE__)
- #include <mach/task.h>
- #include <mach/mach.h>
- #endif
- #define NANOSEC 1000000000
- #define MICROSEC 1000000
- // #define DEBUG_LOG
- #define MEMORY_WARNING_REPORT (1024 * 1024 * 32)
- struct snlua {
- lua_State * L;
- struct skynet_context * ctx;
- size_t mem;
- size_t mem_report;
- size_t mem_limit;
- lua_State * activeL;
- ATOM_INT trap;
- };
- // LUA_CACHELIB may defined in patched lua for shared proto
- #ifdef LUA_CACHELIB
- #define codecache luaopen_cache
- #else
- static int
- cleardummy(lua_State *L) {
- return 0;
- }
- static int
- codecache(lua_State *L) {
- luaL_Reg l[] = {
- { "clear", cleardummy },
- { "mode", cleardummy },
- { NULL, NULL },
- };
- luaL_newlib(L,l);
- lua_getglobal(L, "loadfile");
- lua_setfield(L, -2, "loadfile");
- return 1;
- }
- #endif
- static void
- signal_hook(lua_State *L, lua_Debug *ar) {
- void *ud = NULL;
- lua_getallocf(L, &ud);
- struct snlua *l = (struct snlua *)ud;
- lua_sethook (L, NULL, 0, 0);
- if (ATOM_LOAD(&l->trap)) {
- ATOM_STORE(&l->trap , 0);
- luaL_error(L, "signal 0");
- }
- }
- static void
- switchL(lua_State *L, struct snlua *l) {
- l->activeL = L;
- if (ATOM_LOAD(&l->trap)) {
- lua_sethook(L, signal_hook, LUA_MASKCOUNT, 1);
- }
- }
- static int
- lua_resumeX(lua_State *L, lua_State *from, int nargs, int *nresults) {
- void *ud = NULL;
- lua_getallocf(L, &ud);
- struct snlua *l = (struct snlua *)ud;
- switchL(L, l);
- int err = lua_resume(L, from, nargs, nresults);
- if (ATOM_LOAD(&l->trap)) {
- // wait for lua_sethook. (l->trap == -1)
- while (ATOM_LOAD(&l->trap) >= 0) ;
- }
- switchL(from, l);
- return err;
- }
- static double
- get_time() {
- #if !defined(__APPLE__)
- struct timespec ti;
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti);
- int sec = ti.tv_sec & 0xffff;
- int nsec = ti.tv_nsec;
- return (double)sec + (double)nsec / NANOSEC;
- #else
- struct task_thread_times_info aTaskInfo;
- mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT;
- if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount)) {
- return 0;
- }
- int sec = aTaskInfo.user_time.seconds & 0xffff;
- int msec = aTaskInfo.user_time.microseconds;
- return (double)sec + (double)msec / MICROSEC;
- #endif
- }
- static inline double
- diff_time(double start) {
- double now = get_time();
- if (now < start) {
- return now + 0x10000 - start;
- } else {
- return now - start;
- }
- }
- // coroutine lib, add profile
- /*
- ** Resumes a coroutine. Returns the number of results for non-error
- ** cases or -1 for errors.
- */
- static int auxresume (lua_State *L, lua_State *co, int narg) {
- int status, nres;
- if (!lua_checkstack(co, narg)) {
- lua_pushliteral(L, "too many arguments to resume");
- return -1; /* error flag */
- }
- lua_xmove(L, co, narg);
- status = lua_resumeX(co, L, narg, &nres);
- if (status == LUA_OK || status == LUA_YIELD) {
- if (!lua_checkstack(L, nres + 1)) {
- lua_pop(co, nres); /* remove results anyway */
- lua_pushliteral(L, "too many results to resume");
- return -1; /* error flag */
- }
- lua_xmove(co, L, nres); /* move yielded values */
- return nres;
- }
- else {
- lua_xmove(co, L, 1); /* move error message */
- return -1; /* error flag */
- }
- }
- static int
- timing_enable(lua_State *L, int co_index, lua_Number *start_time) {
- lua_pushvalue(L, co_index);
- lua_rawget(L, lua_upvalueindex(1));
- if (lua_isnil(L, -1)) { // check total time
- lua_pop(L, 1);
- return 0;
- }
- *start_time = lua_tonumber(L, -1);
- lua_pop(L,1);
- return 1;
- }
- static double
- timing_total(lua_State *L, int co_index) {
- lua_pushvalue(L, co_index);
- lua_rawget(L, lua_upvalueindex(2));
- double total_time = lua_tonumber(L, -1);
- lua_pop(L,1);
- return total_time;
- }
- static int
- timing_resume(lua_State *L, int co_index, int n) {
- lua_State *co = lua_tothread(L, co_index);
- lua_Number start_time = 0;
- if (timing_enable(L, co_index, &start_time)) {
- start_time = get_time();
- #ifdef DEBUG_LOG
- fprintf(stderr, "PROFILE [%p] resume %lf\n", co, ti);
- #endif
- lua_pushvalue(L, co_index);
- lua_pushnumber(L, start_time);
- lua_rawset(L, lua_upvalueindex(1)); // set start time
- }
- int r = auxresume(L, co, n);
- if (timing_enable(L, co_index, &start_time)) {
- double total_time = timing_total(L, co_index);
- double diff = diff_time(start_time);
- total_time += diff;
- #ifdef DEBUG_LOG
- fprintf(stderr, "PROFILE [%p] yield (%lf/%lf)\n", co, diff, total_time);
- #endif
- lua_pushvalue(L, co_index);
- lua_pushnumber(L, total_time);
- lua_rawset(L, lua_upvalueindex(2));
- }
- return r;
- }
- static int luaB_coresume (lua_State *L) {
- luaL_checktype(L, 1, LUA_TTHREAD);
- int r = timing_resume(L, 1, lua_gettop(L) - 1);
- if (r < 0) {
- lua_pushboolean(L, 0);
- lua_insert(L, -2);
- return 2; /* return false + error message */
- }
- else {
- lua_pushboolean(L, 1);
- lua_insert(L, -(r + 1));
- return r + 1; /* return true + 'resume' returns */
- }
- }
- static int luaB_auxwrap (lua_State *L) {
- lua_State *co = lua_tothread(L, lua_upvalueindex(3));
- int r = timing_resume(L, lua_upvalueindex(3), lua_gettop(L));
- if (r < 0) {
- int stat = lua_status(co);
- if (stat != LUA_OK && stat != LUA_YIELD)
- lua_closethread(co, L); /* close variables in case of errors */
- if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
- luaL_where(L, 1); /* add extra info, if available */
- lua_insert(L, -2);
- lua_concat(L, 2);
- }
- return lua_error(L); /* propagate error */
- }
- return r;
- }
- static int luaB_cocreate (lua_State *L) {
- lua_State *NL;
- luaL_checktype(L, 1, LUA_TFUNCTION);
- NL = lua_newthread(L);
- lua_pushvalue(L, 1); /* move function to top */
- lua_xmove(L, NL, 1); /* move function from L to NL */
- return 1;
- }
- static int luaB_cowrap (lua_State *L) {
- lua_pushvalue(L, lua_upvalueindex(1));
- lua_pushvalue(L, lua_upvalueindex(2));
- luaB_cocreate(L);
- lua_pushcclosure(L, luaB_auxwrap, 3);
- return 1;
- }
- // profile lib
- static int
- lstart(lua_State *L) {
- if (lua_gettop(L) != 0) {
- lua_settop(L,1);
- luaL_checktype(L, 1, LUA_TTHREAD);
- } else {
- lua_pushthread(L);
- }
- lua_Number start_time = 0;
- if (timing_enable(L, 1, &start_time)) {
- return luaL_error(L, "Thread %p start profile more than once", lua_topointer(L, 1));
- }
- // reset total time
- lua_pushvalue(L, 1);
- lua_pushnumber(L, 0);
- lua_rawset(L, lua_upvalueindex(2));
- // set start time
- lua_pushvalue(L, 1);
- start_time = get_time();
- #ifdef DEBUG_LOG
- fprintf(stderr, "PROFILE [%p] start\n", L);
- #endif
- lua_pushnumber(L, start_time);
- lua_rawset(L, lua_upvalueindex(1));
- return 0;
- }
- static int
- lstop(lua_State *L) {
- if (lua_gettop(L) != 0) {
- lua_settop(L,1);
- luaL_checktype(L, 1, LUA_TTHREAD);
- } else {
- lua_pushthread(L);
- }
- lua_Number start_time = 0;
- if (!timing_enable(L, 1, &start_time)) {
- return luaL_error(L, "Call profile.start() before profile.stop()");
- }
- double ti = diff_time(start_time);
- double total_time = timing_total(L,1);
- lua_pushvalue(L, 1); // push coroutine
- lua_pushnil(L);
- lua_rawset(L, lua_upvalueindex(1));
- lua_pushvalue(L, 1); // push coroutine
- lua_pushnil(L);
- lua_rawset(L, lua_upvalueindex(2));
- total_time += ti;
- lua_pushnumber(L, total_time);
- #ifdef DEBUG_LOG
- fprintf(stderr, "PROFILE [%p] stop (%lf/%lf)\n", lua_tothread(L,1), ti, total_time);
- #endif
- return 1;
- }
- static int
- init_profile(lua_State *L) {
- luaL_Reg l[] = {
- { "start", lstart },
- { "stop", lstop },
- { "resume", luaB_coresume },
- { "wrap", luaB_cowrap },
- { NULL, NULL },
- };
- luaL_newlibtable(L,l);
- lua_newtable(L); // table thread->start time
- lua_newtable(L); // table thread->total time
- lua_newtable(L); // weak table
- lua_pushliteral(L, "kv");
- lua_setfield(L, -2, "__mode");
- lua_pushvalue(L, -1);
- lua_setmetatable(L, -3);
- lua_setmetatable(L, -3);
- luaL_setfuncs(L,l,2);
- return 1;
- }
- /// end of coroutine
- static int
- traceback (lua_State *L) {
- const char *msg = lua_tostring(L, 1);
- if (msg)
- luaL_traceback(L, L, msg, 1);
- else {
- lua_pushliteral(L, "(no error message)");
- }
- return 1;
- }
- static void
- report_launcher_error(struct skynet_context *ctx) {
- // sizeof "ERROR" == 5
- skynet_sendname(ctx, 0, ".launcher", PTYPE_TEXT, 0, "ERROR", 5);
- }
- static const char *
- optstring(struct skynet_context *ctx, const char *key, const char * str) {
- const char * ret = skynet_command(ctx, "GETENV", key);
- if (ret == NULL) {
- return str;
- }
- return ret;
- }
- static int
- init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
- lua_State *L = l->L;
- l->ctx = ctx;
- lua_gc(L, LUA_GCSTOP, 0);
- lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
- lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
- luaL_openlibs(L);
- luaL_requiref(L, "skynet.profile", init_profile, 0);
- int profile_lib = lua_gettop(L);
- // replace coroutine.resume / coroutine.wrap
- lua_getglobal(L, "coroutine");
- lua_getfield(L, profile_lib, "resume");
- lua_setfield(L, -2, "resume");
- lua_getfield(L, profile_lib, "wrap");
- lua_setfield(L, -2, "wrap");
- lua_settop(L, profile_lib-1);
- lua_pushlightuserdata(L, ctx);
- lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context");
- luaL_requiref(L, "skynet.codecache", codecache , 0);
- lua_pop(L,1);
- lua_gc(L, LUA_GCGEN, 0, 0);
- const char *path = optstring(ctx, "lua_path","./lualib/?.lua;./lualib/?/init.lua");
- lua_pushstring(L, path);
- lua_setglobal(L, "LUA_PATH");
- const char *cpath = optstring(ctx, "lua_cpath","./luaclib/?.so");
- lua_pushstring(L, cpath);
- lua_setglobal(L, "LUA_CPATH");
- const char *service = optstring(ctx, "luaservice", "./service/?.lua");
- lua_pushstring(L, service);
- lua_setglobal(L, "LUA_SERVICE");
- const char *preload = skynet_command(ctx, "GETENV", "preload");
- lua_pushstring(L, preload);
- lua_setglobal(L, "LUA_PRELOAD");
- lua_pushcfunction(L, traceback);
- assert(lua_gettop(L) == 1);
- const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
- int r = luaL_loadfile(L,loader);
- if (r != LUA_OK) {
- skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
- report_launcher_error(ctx);
- return 1;
- }
- lua_pushlstring(L, args, sz);
- r = lua_pcall(L,1,0,1);
- if (r != LUA_OK) {
- skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1));
- report_launcher_error(ctx);
- return 1;
- }
- lua_settop(L,0);
- if (lua_getfield(L, LUA_REGISTRYINDEX, "memlimit") == LUA_TNUMBER) {
- size_t limit = lua_tointeger(L, -1);
- l->mem_limit = limit;
- skynet_error(ctx, "Set memory limit to %.2f M", (float)limit / (1024 * 1024));
- lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, "memlimit");
- }
- lua_pop(L, 1);
- lua_gc(L, LUA_GCRESTART, 0);
- return 0;
- }
- static int
- launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
- assert(type == 0 && session == 0);
- struct snlua *l = ud;
- skynet_callback(context, NULL, NULL);
- int err = init_cb(l, context, msg, sz);
- if (err) {
- skynet_command(context, "EXIT", NULL);
- }
- return 0;
- }
- int
- snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
- int sz = strlen(args);
- char * tmp = skynet_malloc(sz);
- memcpy(tmp, args, sz);
- skynet_callback(ctx, l , launch_cb);
- const char * self = skynet_command(ctx, "REG", NULL);
- uint32_t handle_id = strtoul(self+1, NULL, 16);
- // it must be first message
- skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
- return 0;
- }
- static void *
- lalloc(void * ud, void *ptr, size_t osize, size_t nsize) {
- struct snlua *l = ud;
- size_t mem = l->mem;
- l->mem += nsize;
- if (ptr)
- l->mem -= osize;
- if (l->mem_limit != 0 && l->mem > l->mem_limit) {
- if (ptr == NULL || nsize > osize) {
- l->mem = mem;
- return NULL;
- }
- }
- if (l->mem > l->mem_report) {
- l->mem_report *= 2;
- skynet_error(l->ctx, "Memory warning %.2f M", (float)l->mem / (1024 * 1024));
- }
- return skynet_lalloc(ptr, osize, nsize);
- }
- struct snlua *
- snlua_create(void) {
- struct snlua * l = skynet_malloc(sizeof(*l));
- memset(l,0,sizeof(*l));
- l->mem_report = MEMORY_WARNING_REPORT;
- l->mem_limit = 0;
- l->L = lua_newstate(lalloc, l);
- l->activeL = NULL;
- ATOM_INIT(&l->trap , 0);
- return l;
- }
- void
- snlua_release(struct snlua *l) {
- lua_close(l->L);
- skynet_free(l);
- }
- void
- snlua_signal(struct snlua *l, int signal) {
- skynet_error(l->ctx, "recv a signal %d", signal);
- if (signal == 0) {
- if (ATOM_LOAD(&l->trap) == 0) {
- // only one thread can set trap ( l->trap 0->1 )
- if (!ATOM_CAS(&l->trap, 0, 1))
- return;
- lua_sethook (l->activeL, signal_hook, LUA_MASKCOUNT, 1);
- // finish set ( l->trap 1 -> -1 )
- ATOM_CAS(&l->trap, 1, -1);
- }
- } else if (signal == 1) {
- skynet_error(l->ctx, "Current Memory %.3fK", (float)l->mem / 1024);
- }
- }
|