接上一篇分析《skynet服务之launcher》,本篇咱们继续来分析一下lua中的协程如何与服务有机结合的,关于lua中协程的解释参见本文《lua中协程的理解》; html
上一篇分析到,当一个lua服务收到消息后,在lua层,最终会执行到raw_dispatch_message函数,代码以下: session
local function raw_dispatch_message(prototype, msg, sz, session, source)
-- skynet.PTYPE_RESPONSE = 1, read skynet.h
if prototype == 1 then --回应包
local co = session_id_coroutine[session]
if co == "BREAK" then
session_id_coroutine[session] = nil
elseif co == nil then
unknown_response(session, source, msg, sz)
else
session_id_coroutine[session] = nil
suspend(co, coroutine_resume(co, true, msg, sz))
end
else
local p = proto[prototype]
if p == nil then
if session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, prototype)
end
return
end
local f = p.dispatch
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)
session_coroutine_id[co] = session
session_coroutine_address[co] = source
suspend(co, coroutine_resume(co, session,source, p.unpack(msg,sz)))
elseif session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, proto[prototype].name)
end
end
end 函数
函数先经过f = p.dispatch取到特定服务的消息处理函数,而后调用co_create(f)建立一个协程,该函数实现以下: lua
local coroutine_pool = setmetatable({}, { __mode = "kv" })
local function co_create(f)
local co = table.remove(coroutine_pool)
if co == nil then
co = coroutine.create(function(...)
f(...)
while true do
f = nil
coroutine_pool[#coroutine_pool+1] = co
f = coroutine_yield "EXIT"
f(coroutine_yield())
end
end)
else
coroutine_resume(co, f)
end
return co
end spa
以上代码的中使用了弱表来做为协程池,关于lua中的弱表请参考《lua中的弱表理解》; prototype
上述函数先经过table.remove(coroutine_pool)从coroutine_pool表中pop最后一个元素,即一个协程, 协程
若是不存在协程,就会执行coroutine.create(function(...))来建立一个新的协程,协程执行函数接受可变参数, htm
注意:此时并未执行协程函数; blog
若是从协程库中取到了一个协程,直接调用了coroutine_resume(co, f),这个函数对应函数为: rem
local coroutine_resume = profile.resume
而profile.resume对应C库函数lresume(…),该函数实现代码以下:
static int
lresume(lua_State *L) {
lua_pushvalue(L,1);
return timing_resume(L);
}
接着调用了timing_resume(L),函数实现代码以下:
static int
timing_resume(lua_State *L) {
lua_pushvalue(L, -1);
lua_rawget(L, lua_upvalueindex(2));
if (lua_isnil(L, -1)) { // check total time
lua_pop(L,2); // pop from coroutine
} else {
lua_pop(L,1);
double ti = get_time();
#ifdef DEBUG_LOG
fprintf(stderr, "PROFILE [%p] resume %lf\n", lua_tothread(L, -1), ti);
#endif
lua_pushnumber(L, ti);
lua_rawset(L, lua_upvalueindex(1)); // set start time
}
lua_CFunction co_resume = lua_tocfunction(L, lua_upvalueindex(3));
return co_resume(L);}