├── Makefile ├── test ├── config ├── main.lua └── echo_reload.lua ├── README.md ├── lualib └── reload.lua ├── LICENSE └── lua-reload.c /Makefile: -------------------------------------------------------------------------------- 1 | # Use your path 2 | SKYNET_PATH = $(HOME)/skynet 3 | TARGET = reload.so 4 | 5 | $(TARGET) : lua-reload.c 6 | gcc -Wall -g --shared -fPIC -o $@ $^ -I$(SKYNET_PATH)/skynet-src 7 | 8 | clean : 9 | rm $(TARGET) 10 | -------------------------------------------------------------------------------- /test/config: -------------------------------------------------------------------------------- 1 | root = "$HOME/skynet/" 2 | thread = 8 3 | harbor = 0 4 | luaservice = root.."service/?.lua;./test/?.lua" 5 | lualoader = root.."lualib/loader.lua" 6 | cpath = root.."cservice/?.so" 7 | lua_path = root.."lualib/?.lua;./lualib/?.lua" 8 | lua_cpath = root.."luaclib/?.so;./?.so" 9 | start = "main" 10 | -------------------------------------------------------------------------------- /test/main.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | skynet.start(function() 4 | local s = skynet.newservice "echo_reload" 5 | local function echo(msg) 6 | return skynet.call(s, "lua", msg) 7 | end 8 | local i = 0 9 | while true do 10 | print(pcall(echo, i)) 11 | i = i+ 1 12 | end 13 | end) 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What's this ? 2 | 3 | A library for reload a lua service. 4 | 5 | ## Build 6 | 7 | Modify the Makefile, change SKYNET_PATH to your path of skynet. The default path is $(HOME)/skynet . 8 | 9 | If you are not using linux, make it by yourself. 10 | 11 | ## Test 12 | 13 | ``` 14 | $(HOME)/skynet test/config 15 | ``` 16 | -------------------------------------------------------------------------------- /test/echo_reload.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local reload = require "reload" 3 | 4 | local arg = ... 5 | 6 | A = { tostring(arg) } 7 | setmetatable(A , { __gc = function(t) skynet.error("Version gone :" .. t[1]) end }) 8 | 9 | 10 | skynet.start(function() 11 | local version = tonumber(arg) or 0 12 | skynet.error(string.format("Version %d", version)) 13 | skynet.dispatch("lua", function(_,_,msg) 14 | skynet.sleep(100) 15 | skynet.ret(skynet.pack(msg)) 16 | end) 17 | skynet.timeout(450, function() 18 | reload(version + 1) 19 | end) 20 | end) 21 | -------------------------------------------------------------------------------- /lualib/reload.lua: -------------------------------------------------------------------------------- 1 | local core = require "reload.core" 2 | local skynet = require "skynet" 3 | 4 | local function get(f, name) 5 | local i = 1 6 | repeat 7 | local n, v = debug.getupvalue(f, i) 8 | if n == name then 9 | return v 10 | end 11 | i = i+1 12 | until n == nil 13 | end 14 | 15 | local function raise_error() 16 | local session_coroutine_id = get(skynet.exit, "session_coroutine_id") 17 | local session_coroutine_address = get(skynet.exit, "session_coroutine_address") 18 | for co, session in pairs(session_coroutine_id) do 19 | local address = session_coroutine_address[co] 20 | if session~=0 and address then 21 | skynet.redirect(address, 0, skynet.PTYPE_ERROR, session, "") 22 | end 23 | end 24 | local unresponse = get(skynet.exit, "unresponse") 25 | for resp in pairs(unresponse) do 26 | resp(false) 27 | end 28 | end 29 | 30 | local function reload(...) 31 | local args = SERVICE_NAME .. " " .. table.concat({...}, " ") 32 | local L = core.reload(args) 33 | core.link(L) 34 | raise_error() 35 | coroutine.yield "QUIT" -- never return 36 | end 37 | 38 | return reload 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 codingnow.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lua-reload.c: -------------------------------------------------------------------------------- 1 | #include "skynet.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | // LUA_CACHELIB may defined in patched lua for shared proto 10 | #ifdef LUA_CACHELIB 11 | 12 | #define codecache luaopen_cache 13 | 14 | #else 15 | 16 | static int 17 | cleardummy(lua_State *L) { 18 | return 0; 19 | } 20 | 21 | static int 22 | codecache(lua_State *L) { 23 | luaL_Reg l[] = { 24 | { "clear", cleardummy }, 25 | { "mode", cleardummy }, 26 | { NULL, NULL }, 27 | }; 28 | luaL_newlib(L,l); 29 | lua_getglobal(L, "loadfile"); 30 | lua_setfield(L, -2, "loadfile"); 31 | return 1; 32 | } 33 | 34 | #endif 35 | 36 | static int 37 | traceback (lua_State *L) { 38 | const char *msg = lua_tostring(L, 1); 39 | if (msg) 40 | luaL_traceback(L, L, msg, 1); 41 | else { 42 | lua_pushliteral(L, "(no error message)"); 43 | } 44 | return 1; 45 | } 46 | 47 | static const char * 48 | optstring(struct skynet_context *ctx, const char *key, const char * str) { 49 | const char * ret = skynet_command(ctx, "GETENV", key); 50 | if (ret == NULL) { 51 | return str; 52 | } 53 | return ret; 54 | } 55 | 56 | static struct lua_State * 57 | load_service(struct skynet_context *ctx, const char * args, size_t sz, lua_State *oL) { 58 | void *ud = NULL; 59 | lua_Alloc alloc = lua_getallocf(oL, &ud); 60 | lua_State *L = lua_newstate(alloc, ud); 61 | lua_gc(L, LUA_GCSTOP, 0); 62 | lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ 63 | lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); 64 | luaL_openlibs(L); 65 | lua_pushlightuserdata(L, ctx); 66 | lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context"); 67 | luaL_requiref(L, "skynet.codecache", codecache , 0); 68 | lua_getfield(L, -1, "mode"); 69 | if (lua_type(L, -1) == LUA_TFUNCTION) { 70 | lua_pushstring(L, "OFF"); 71 | lua_pcall(L, 1, 0, 0); 72 | } else { 73 | lua_pop(L, 1); 74 | } 75 | 76 | lua_pop(L,1); 77 | 78 | const char *path = optstring(ctx, "lua_path","./lualib/?.lua;./lualib/?/init.lua"); 79 | lua_pushstring(L, path); 80 | lua_setglobal(L, "LUA_PATH"); 81 | const char *cpath = optstring(ctx, "lua_cpath","./luaclib/?.so"); 82 | lua_pushstring(L, cpath); 83 | lua_setglobal(L, "LUA_CPATH"); 84 | const char *service = optstring(ctx, "luaservice", "./service/?.lua"); 85 | lua_pushstring(L, service); 86 | lua_setglobal(L, "LUA_SERVICE"); 87 | const char *preload = skynet_command(ctx, "GETENV", "preload"); 88 | lua_pushstring(L, preload); 89 | lua_setglobal(L, "LUA_PRELOAD"); 90 | 91 | lua_pushcfunction(L, traceback); 92 | assert(lua_gettop(L) == 1); 93 | 94 | const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua"); 95 | 96 | int r = luaL_loadfile(L,loader); 97 | if (r != LUA_OK) { 98 | skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1)); 99 | lua_close(L); 100 | return NULL; 101 | } 102 | lua_pushlstring(L, args, sz); 103 | r = lua_pcall(L,1,0,1); 104 | if (r != LUA_OK) { 105 | skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1)); 106 | lua_close(L); 107 | // NOTICE: If the script change the skynet callback, it may cause the core dump. 108 | return NULL; 109 | } 110 | lua_settop(L,0); 111 | 112 | lua_gc(L, LUA_GCRESTART, 0); 113 | 114 | return L; 115 | } 116 | 117 | static int 118 | lreload(lua_State *L) { 119 | size_t sz; 120 | const char * args = luaL_checklstring(L, 1, &sz); 121 | lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context"); 122 | luaL_checktype(L, -1, LUA_TLIGHTUSERDATA); 123 | struct skynet_context *ctx = lua_touserdata(L, -1); 124 | lua_pop(L, 1); 125 | struct lua_State *nL = load_service(ctx, args, (int)sz, L); 126 | if (nL == NULL) { 127 | return luaL_error(L, "load [%s] failed", args); 128 | } 129 | lua_pushlightuserdata(L, nL); 130 | 131 | return 1; 132 | } 133 | 134 | static int 135 | deleteL(lua_State *L) { 136 | lua_rawgeti(L, 1, 1); 137 | lua_State *nL = lua_touserdata(L, -1); 138 | if (nL) { 139 | lua_close(nL); 140 | lua_pushnil(L); 141 | lua_rawseti(L, 1, 1); 142 | } 143 | return 0; 144 | } 145 | 146 | static void 147 | clear_parent(lua_State *L) { 148 | lua_getfield(L, LUA_REGISTRYINDEX, "skynet_parent"); 149 | lua_State *oL = lua_touserdata(L, -1); 150 | lua_pop(L, 1); 151 | if (oL == NULL) { 152 | return; 153 | } 154 | 155 | lua_getfield(oL, LUA_REGISTRYINDEX, "skynet_parent"); 156 | lua_State *gL = lua_touserdata(oL, -1); 157 | lua_pop(oL, 1); 158 | if (gL == NULL) { 159 | return; 160 | } 161 | 162 | lua_getfield(oL, LUA_REGISTRYINDEX, "skynet_child"); // oL[skynet_child][1] = nil 163 | lua_pushnil(oL); // don't gc me 164 | lua_rawseti(oL, -2, 1); 165 | lua_pop(oL, 1); 166 | 167 | lua_getfield(gL, LUA_REGISTRYINDEX, "skynet_child"); // gL[skynet_child][1] = L 168 | lua_pushlightuserdata(gL, L); 169 | lua_rawseti(gL, -2, 1); 170 | lua_pop(gL, 1); 171 | 172 | lua_pushlightuserdata(L, gL); 173 | lua_setfield(L, LUA_REGISTRYINDEX, "skynet_parent"); 174 | 175 | lua_close(oL); 176 | } 177 | 178 | static int 179 | llink(lua_State *L) { 180 | luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); // new L 181 | lua_State *nL = lua_touserdata(L, 1); 182 | 183 | lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); 184 | lua_State *mL = lua_tothread(L, -1); 185 | lua_pop(L, 1); 186 | if (mL) { 187 | lua_pushlightuserdata(nL, mL); 188 | lua_setfield(nL, LUA_REGISTRYINDEX, "skynet_parent"); 189 | } 190 | 191 | lua_createtable(L, 1, 0); 192 | lua_pushlightuserdata(L, nL); 193 | lua_rawseti(L, -2, 1); 194 | 195 | lua_createtable(L, 0, 1); 196 | lua_pushcfunction(L, deleteL); 197 | lua_setfield(L, -2, "__gc"); 198 | 199 | lua_setmetatable(L, -2); 200 | 201 | lua_setfield(L, LUA_REGISTRYINDEX, "skynet_child"); 202 | 203 | clear_parent(L); 204 | 205 | return 0; 206 | } 207 | 208 | int 209 | luaopen_reload_core(lua_State *L) { 210 | luaL_checkversion(L); 211 | 212 | luaL_Reg l[] = { 213 | { "reload", lreload }, 214 | { "link", llink }, 215 | { NULL, NULL }, 216 | }; 217 | 218 | luaL_newlib(L,l); 219 | return 1; 220 | } 221 | --------------------------------------------------------------------------------