add skynet.response for delay response

This commit is contained in:
Cloud Wu
2014-07-30 12:10:30 +08:00
parent c78466b486
commit ce5adba5b2
13 changed files with 182 additions and 96 deletions

View File

@@ -1,3 +1,10 @@
Dev version
-----------
* add sharedata
* bugfix: service exit before init would not report back
* add skynet.response and check multicall skynet.ret
* skynet.newservice throw error when lanuch faild
v0.5.0 (2014-7-28) v0.5.0 (2014-7-28)
----------- -----------
* skynet.exit will quit service immediately. * skynet.exit will quit service immediately.

View File

@@ -299,6 +299,26 @@ lpackstring(lua_State *L) {
return 1; return 1;
} }
static int
ltrash(lua_State *L) {
int t = lua_type(L,1);
switch (t) {
case LUA_TSTRING: {
break;
}
case LUA_TLIGHTUSERDATA: {
void * msg = lua_touserdata(L,1);
luaL_checkinteger(L,2);
skynet_free(msg);
break;
}
default:
luaL_error(L, "skynet.trash invalid param %s", lua_typename(L,t));
}
return 0;
}
int int
luaopen_skynet_c(lua_State *L) { luaopen_skynet_c(lua_State *L) {
luaL_checkversion(L); luaL_checkversion(L);
@@ -314,6 +334,7 @@ luaopen_skynet_c(lua_State *L) {
{ "pack", _luaseri_pack }, { "pack", _luaseri_pack },
{ "unpack", _luaseri_unpack }, { "unpack", _luaseri_unpack },
{ "packstring", lpackstring }, { "packstring", lpackstring },
{ "trash" , ltrash },
{ "callback", _callback }, { "callback", _callback },
{ NULL, NULL }, { NULL, NULL },
}; };

View File

@@ -14,7 +14,7 @@ local function monitor(name, obj, cobj)
while true do while true do
newobj = skynet.call(service, "lua", "monitor", name, newobj) newobj = skynet.call(service, "lua", "monitor", name, newobj)
if newobj == nil then if newobj == nil then
return break
end end
sd.update(obj, newobj) sd.update(obj, newobj)
end end

View File

@@ -43,12 +43,14 @@ end
local session_id_coroutine = {} local session_id_coroutine = {}
local session_coroutine_id = {} local session_coroutine_id = {}
local session_coroutine_address = {} local session_coroutine_address = {}
local session_response = {}
local wakeup_session = {} local wakeup_session = {}
local sleep_session = {} local sleep_session = {}
local watching_service = {} local watching_service = {}
local watching_session = {} local watching_session = {}
local dead_service = {}
local error_queue = {} local error_queue = {}
-- suspend is function -- suspend is function
@@ -73,7 +75,9 @@ local function _error_dispatch(error_session, error_source)
if error_session == 0 then if error_session == 0 then
-- service is down -- service is down
-- Don't remove from watching_service , because user may call dead service -- Don't remove from watching_service , because user may call dead service
watching_service[error_source] = false if watching_service[error_source] then
dead_service[error_source] = true
end
for session, srv in pairs(watching_session) do for session, srv in pairs(watching_session) do
if srv == error_source then if srv == error_source then
table.insert(error_queue, session) table.insert(error_queue, session)
@@ -122,6 +126,18 @@ local function dispatch_wakeup()
end end
end end
local function release_watching(address)
local ref = watching_service[address]
if ref then
ref = ref - 1
if ref > 0 then
watching_service[address] = ref
else
watching_service[address] = nil
end
end
end
-- suspend is local function -- suspend is local function
function suspend(co, result, command, param, size) function suspend(co, result, command, param, size)
if not result then if not result then
@@ -142,15 +158,50 @@ function suspend(co, result, command, param, size)
elseif command == "RETURN" then elseif command == "RETURN" then
local co_session = session_coroutine_id[co] local co_session = session_coroutine_id[co]
local co_address = session_coroutine_address[co] local co_address = session_coroutine_address[co]
if param == nil then if param == nil or session_response[co] then
error(debug.traceback(co)) error(debug.traceback(co))
end end
c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) session_response[co] = true
return suspend(co, coroutine.resume(co)) local ret
if not dead_service[co_address] then
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) >= 0
elseif size == nil then
c.trash(param, size)
ret = false
end
return suspend(co, coroutine.resume(co, ret))
elseif command == "RESPONSE" then
local co_session = session_coroutine_id[co]
local co_address = session_coroutine_address[co]
if session_response[co] then
error(debug.traceback(co))
end
local f = param
local function response(ok, ...)
local ret
if not dead_service[co_address] then
if ok then
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, f(...)) >=0
else
ret = c.send(co_address, skynet.PTYPE_ERROR, co_session, "") >=0
end
else
ret = false
end
release_watching(co_address)
f = nil
return ret
end
watching_service[co_address] = watching_service[co_address] + 1
session_response[co] = response
return suspend(co, coroutine.resume(co, response))
elseif command == "EXIT" then elseif command == "EXIT" then
-- coroutine exit -- coroutine exit
local address = session_coroutine_address[co]
release_watching(address)
session_coroutine_id[co] = nil session_coroutine_id[co] = nil
session_coroutine_address[co] = nil session_coroutine_address[co] = nil
session_response[co] = nil
elseif command == "QUIT" then elseif command == "QUIT" then
-- service exit -- service exit
return return
@@ -259,13 +310,21 @@ end
function skynet.exit() function skynet.exit()
skynet.send(".launcher","lua","REMOVE",skynet.self()) skynet.send(".launcher","lua","REMOVE",skynet.self())
-- report the sources that call me
for co, session in pairs(session_coroutine_id) do for co, session in pairs(session_coroutine_id) do
local address = session_coroutine_address[co] local address = session_coroutine_address[co]
local self = skynet.self()
if session~=0 and address then if session~=0 and address then
skynet.redirect(address, self, "error", session, "") c.redirect(address, 0, skynet.PTYPE_ERROR, session, "")
end end
end end
-- report the sources I call but haven't return
local tmp = {}
for session, address in pairs(watching_session) do
tmp[address] = true
end
for address in pairs(tmp) do
c.redirect(address, 0, skynet.PTYPE_ERROR, 0, "")
end
c.command("EXIT") c.command("EXIT")
-- quit service -- quit service
coroutine_yield "QUIT" coroutine_yield "QUIT"
@@ -294,9 +353,6 @@ end
function skynet.send(addr, typename, ...) function skynet.send(addr, typename, ...)
local p = proto[typename] local p = proto[typename]
if watching_service[addr] == false then
error("Service is dead")
end
return c.send(addr, p.id, 0 , p.pack(...)) return c.send(addr, p.id, 0 , p.pack(...))
end end
@@ -321,9 +377,6 @@ end
function skynet.call(addr, typename, ...) function skynet.call(addr, typename, ...)
local p = proto[typename] local p = proto[typename]
if watching_service[addr] == false then
error("Service is dead")
end
local session = c.send(addr, p.id , nil , p.pack(...)) local session = c.send(addr, p.id , nil , p.pack(...))
if session == nil then if session == nil then
error("call to invalid address " .. skynet.address(addr)) error("call to invalid address " .. skynet.address(addr))
@@ -333,16 +386,18 @@ end
function skynet.rawcall(addr, typename, msg, sz) function skynet.rawcall(addr, typename, msg, sz)
local p = proto[typename] local p = proto[typename]
if watching_service[addr] == false then
error("Service is dead")
end
local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address") local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address")
return yield_call(addr, session) return yield_call(addr, session)
end end
function skynet.ret(msg, sz) function skynet.ret(msg, sz)
msg = msg or "" msg = msg or ""
coroutine_yield("RETURN", msg, sz) return coroutine_yield("RETURN", msg, sz)
end
function skynet.response(pack)
pack = pack or skynet.pack
return coroutine_yield("RESPONSE", pack)
end end
function skynet.retpack(...) function skynet.retpack(...)
@@ -412,6 +467,12 @@ local function raw_dispatch_message(prototype, msg, sz, session, source, ...)
local p = assert(proto[prototype], prototype) local p = assert(proto[prototype], prototype)
local f = p.dispatch local f = p.dispatch
if f then if f then
local ref = watching_service[source]
if ref then
watching_service[source] = ref + 1
else
watching_service[source] = 1
end
local co = co_create(f) local co = co_create(f)
session_coroutine_id[co] = session session_coroutine_id[co] = session
session_coroutine_address[co] = source session_coroutine_address[co] = source

View File

@@ -13,31 +13,31 @@ skynet.start(function()
standalone = true standalone = true
skynet.setenv("standalone", "true") skynet.setenv("standalone", "true")
local slave = skynet.newservice "cdummy" local ok, slave = pcall(skynet.newservice, "cdummy")
if slave == nil then if not ok then
skynet.abort() skynet.abort()
end end
skynet.name(".slave", slave) skynet.name(".slave", slave)
else else
if standalone then if standalone then
if not skynet.newservice "cmaster" then if not pcall(skynet.newservice,"cmaster") then
skynet.abort() skynet.abort()
end end
end end
local slave = skynet.newservice "cslave" local ok, slave = pcall(skynet.newservice, "cslave")
if slave == nil then if not ok then
skynet.abort() skynet.abort()
end end
skynet.name(".slave", slave) skynet.name(".slave", slave)
end end
if standalone then if standalone then
local datacenter = assert(skynet.newservice "datacenterd") local datacenter = skynet.newservice "datacenterd"
skynet.name("DATACENTER", datacenter) skynet.name("DATACENTER", datacenter)
end end
assert(skynet.newservice "service_mgr") skynet.newservice "service_mgr"
assert(skynet.newservice(skynet.getenv "start" or "main")) pcall(skynet.newservice,skynet.getenv "start" or "main")
skynet.exit() skynet.exit()
end) end)

View File

@@ -7,10 +7,7 @@ local function console_main_loop()
while true do while true do
local cmdline = socket.readline(stdin, "\n") local cmdline = socket.readline(stdin, "\n")
if cmdline ~= "" then if cmdline ~= "" then
local handle = skynet.newservice(cmdline) pcall(skynet.newservice,cmdline)
if handle == nil then
print("Launch error:",cmdline)
end
end end
end end
socket.unlock(stdin) socket.unlock(stdin)

View File

@@ -28,7 +28,7 @@ local function monitor_clear(id)
if v then if v then
monitor[id] = nil monitor[id] = nil
for _, v in ipairs(v) do for _, v in ipairs(v) do
skynet.redirect(v.address, 0, "response", v.session, "") v()
end end
end end
end end
@@ -149,30 +149,30 @@ local function monitor_harbor(master_fd)
end end
end end
function harbor.REGISTER(_,_, fd, name, handle) function harbor.REGISTER(fd, name, handle)
assert(globalname[name] == nil) assert(globalname[name] == nil)
globalname[name] = handle globalname[name] = handle
socket.write(fd, pack_package("R", name, handle)) socket.write(fd, pack_package("R", name, handle))
skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name) skynet.redirect(harbor_service, handle, "harbor", 0, "N " .. name)
end end
function harbor.LINK(session, source, fd, id) function harbor.LINK(fd, id)
if slaves[id] then if slaves[id] then
if monitor[id] == nil then if monitor[id] == nil then
monitor[id] = {} monitor[id] = {}
end end
table.insert(monitor[id], { address = source, session = session }) table.insert(monitor[id], skynet.response(true))
else else
skynet.ret() skynet.ret()
end end
end end
function harbor.CONNECT(session, source, fd, id) function harbor.CONNECT(fd, id)
if not slaves[id] then if not slaves[id] then
if monitor[id] == nil then if monitor[id] == nil then
monitor[id] = {} monitor[id] = {}
end end
table.insert(monitor[id], { address = source, session = session }) table.insert(monitor[id], skynet.response(true))
else else
skynet.ret() skynet.ret()
end end
@@ -186,9 +186,9 @@ skynet.start(function()
skynet.error("slave connect to master " .. tostring(master_addr)) skynet.error("slave connect to master " .. tostring(master_addr))
local master_fd = socket.open(master_addr) local master_fd = socket.open(master_addr)
skynet.dispatch("lua", function (session,source,command,...) skynet.dispatch("lua", function (_,_,command,...)
local f = assert(harbor[command]) local f = assert(harbor[command])
f(session, source, master_fd, ...) f(master_fd, ...)
end) end)
skynet.dispatch("text", monitor_harbor(master_fd)) skynet.dispatch("text", monitor_harbor(master_fd))

View File

@@ -45,10 +45,8 @@ local function wakeup(db, key1, key2, value, ...)
db[key1] = nil db[key1] = nil
if value then if value then
-- throw error because can't wake up a branch -- throw error because can't wake up a branch
for _,v in ipairs(q) do for _,response in ipairs(q) do
local session = v[1] response(false)
local source = v[2]
skynet.redirect(source, 0, "error", session, "")
end end
else else
return q return q
@@ -66,15 +64,13 @@ function command.UPDATE(...)
end end
local q = wakeup(wait_queue, ...) local q = wakeup(wait_queue, ...)
if q then if q then
for _, v in ipairs(q) do for _, response in ipairs(q) do
local session = v[1] response(true,value)
local source = v[2]
skynet.redirect(source, 0, "response", session, skynet.pack(value))
end end
end end
end end
local function waitfor(session, source, db, key1, key2, ...) local function waitfor(db, key1, key2, ...)
if key2 == nil then if key2 == nil then
-- push queue -- push queue
local q = db[key1] local q = db[key1]
@@ -84,7 +80,7 @@ local function waitfor(session, source, db, key1, key2, ...)
else else
assert(q[mode] == "queue") assert(q[mode] == "queue")
end end
table.insert(q, { session, source }) table.insert(q, skynet.response())
else else
local q = db[key1] local q = db[key1]
if q == nil then if q == nil then
@@ -93,18 +89,18 @@ local function waitfor(session, source, db, key1, key2, ...)
else else
assert(q[mode] == "branch") assert(q[mode] == "branch")
end end
return waitfor(session, source, q, key2, ...) return waitfor(q, key2, ...)
end end
end end
skynet.start(function() skynet.start(function()
skynet.dispatch("lua", function (session, source, cmd, ...) skynet.dispatch("lua", function (_, _, cmd, ...)
if cmd == "WAIT" then if cmd == "WAIT" then
local ret = command.QUERY(...) local ret = command.QUERY(...)
if ret then if ret then
skynet.ret(skynet.pack(ret)) skynet.ret(skynet.pack(ret))
else else
waitfor(session, source, wait_queue, ...) waitfor(wait_queue, ...)
end end
else else
local f = assert(command[cmd]) local f = assert(command[cmd])

View File

@@ -122,8 +122,8 @@ function COMMAND.clearcache()
end end
function COMMAND.start(...) function COMMAND.start(...)
local addr = skynet.newservice(...) local ok, addr = pcall(skynet.newservice, ...)
if addr then if ok then
return { [skynet.address(addr)] = ... } return { [skynet.address(addr)] = ... }
else else
return "Failed" return "Failed"
@@ -131,8 +131,8 @@ function COMMAND.start(...)
end end
function COMMAND.snax(...) function COMMAND.snax(...)
local s = snax.newservice(...) local ok, s = pcall(snax.newservice, ...)
if s then if ok then
local addr = s.handle local addr = s.handle
return { [skynet.address(addr)] = ... } return { [skynet.address(addr)] = ... }
else else

View File

@@ -28,7 +28,7 @@ function command.STAT()
return list return list
end end
function command.INFO(_, _, handle) function command.INFO(_, handle)
handle = handle_to_address(handle) handle = handle_to_address(handle)
if services[handle] == nil then if services[handle] == nil then
return return
@@ -38,7 +38,7 @@ function command.INFO(_, _, handle)
end end
end end
function command.TASK(_, _, handle) function command.TASK(_, handle)
handle = handle_to_address(handle) handle = handle_to_address(handle)
if services[handle] == nil then if services[handle] == nil then
return return
@@ -48,7 +48,7 @@ function command.TASK(_, _, handle)
end end
end end
function command.KILL(_, _, handle) function command.KILL(_, handle)
handle = handle_to_address(handle) handle = handle_to_address(handle)
skynet.kill(handle) skynet.kill(handle)
local ret = { [skynet.address(handle)] = tostring(services[handle]) } local ret = { [skynet.address(handle)] = tostring(services[handle]) }
@@ -72,18 +72,29 @@ function command.GC()
return command.MEM() return command.MEM()
end end
function command.REMOVE(_,_, handle) function command.REMOVE(_, handle)
services[handle] = nil services[handle] = nil
local response = instance[handle]
if response then
-- instance is dead
response(false)
instance[handle] = nil
end
-- don't return (skynet.ret) because the handle may exit -- don't return (skynet.ret) because the handle may exit
return NORET return NORET
end end
function command.LAUNCH(address, session, service, ...) local function return_string(str)
return str
end
function command.LAUNCH(_, service, ...)
local param = table.concat({...}, " ") local param = table.concat({...}, " ")
local inst = skynet.launch(service, param) local inst = skynet.launch(service, param)
if inst then if inst then
services[inst] = service .. " " .. param services[inst] = service .. " " .. param
instance[inst] = { session = session, address = address } instance[inst] = skynet.response(return_string)
else else
skynet.ret("") -- launch failed skynet.ret("") -- launch failed
end end
@@ -93,9 +104,9 @@ end
function command.ERROR(address) function command.ERROR(address)
-- see serivce-src/service_lua.c -- see serivce-src/service_lua.c
-- init failed -- init failed
local reply = instance[address] local response = instance[address]
if reply then if response then
skynet.redirect(reply.address , 0, "response", reply.session, "") response(false)
instance[address] = nil instance[address] = nil
end end
services[address] = nil services[address] = nil
@@ -104,9 +115,9 @@ end
function command.LAUNCHOK(address) function command.LAUNCHOK(address)
-- init notice -- init notice
local reply = instance[address] local response = instance[address]
if reply then if response then
skynet.redirect(reply.address , 0, "response", reply.session, skynet.address(address)) response(true, skynet.address(address))
instance[address] = nil instance[address] = nil
end end
@@ -127,7 +138,7 @@ skynet.register_protocol {
else else
-- launch request -- launch request
local service, param = string.match(cmd,"([^ ]+) (.*)") local service, param = string.match(cmd,"([^ ]+) (.*)")
command.LAUNCH(address, session, service, param) command.LAUNCH(_, service, param)
end end
end, end,
} }
@@ -136,7 +147,7 @@ skynet.dispatch("lua", function(session, address, cmd , ...)
cmd = string.upper(cmd) cmd = string.upper(cmd)
local f = command[cmd] local f = command[cmd]
if f then if f then
local ret = f(address, session, ...) local ret = f(address, ...)
if ret ~= NORET then if ret ~= NORET then
skynet.ret(skynet.pack(ret)) skynet.ret(skynet.pack(ret))
end end

View File

@@ -55,10 +55,8 @@ function CMD.delete(name)
assert(objmap[v.obj]) assert(objmap[v.obj])
objmap[v.obj] = true objmap[v.obj] = true
sharedata.host.decref(v.obj) sharedata.host.decref(v.obj)
for _,v in ipairs(v.watch) do for _,response in ipairs(v.watch) do
local session = v[1] response(true)
local address = v[2]
skynet.redirect(address, 0, "response", session, skynet.pack(nil))
end end
end end
@@ -90,21 +88,19 @@ function CMD.update(name, t)
local newobj = pool[name].obj local newobj = pool[name].obj
if watch then if watch then
sharedata.host.markdirty(oldcobj) sharedata.host.markdirty(oldcobj)
for _,v in ipairs(watch) do for _,response in ipairs(watch) do
local session = v[1] response(true, newobj)
local address = v[2]
skynet.redirect(address, 0, "response", session, skynet.pack(newobj))
end end
end end
end end
function CMD.monitor(session, address, name, obj) function CMD.monitor(name, obj)
local v = assert(pool[name]) local v = assert(pool[name])
if obj ~= v.obj then if obj ~= v.obj then
return v.obj return v.obj
end end
table.insert(v.watch, { session, address }) table.insert(v.watch, skynet.response())
return NORET return NORET
end end
@@ -113,12 +109,7 @@ skynet.start(function()
skynet.fork(collectobj) skynet.fork(collectobj)
skynet.dispatch("lua", function (session, source ,cmd, ...) skynet.dispatch("lua", function (session, source ,cmd, ...)
local f = assert(CMD[cmd]) local f = assert(CMD[cmd])
local r local r = f(...)
if cmd == "monitor" then
r = f(session, source, ...)
else
r = f(...)
end
if r ~= NORET then if r ~= NORET then
skynet.ret(skynet.pack(r)) skynet.ret(skynet.pack(r))
end end

View File

@@ -1,10 +0,0 @@
local skynet = require "skynet"
skynet.start(function()
local ping = skynet.newservice("pingserver")
skynet.timeout(0,function()
print(skynet.call(ping,"lua","PING","ping"))
end)
print(skynet.blockcall(ping,"lua","HELLO"))
end)

View File

@@ -11,6 +11,15 @@ skynet.start(function()
end) end)
end) end)
elseif mode == "dead" then
skynet.start(function()
skynet.dispatch("lua", function (...)
skynet.sleep(100)
print("return", skynet.ret "")
end)
end)
else else
skynet.start(function() skynet.start(function()
@@ -20,6 +29,9 @@ else
skynet.call(test,"lua", "dead call") skynet.call(test,"lua", "dead call")
end)) end))
skynet.exit() local dead = skynet.newservice(SERVICE_NAME, "dead") -- launch self in dead mode
skynet.timeout(0, skynet.exit) -- exit after a while, so the call never return
skynet.call(dead, "lua", "whould not return")
end) end)
end end