接上一篇分析《skynet服务之launcher》,本篇我们继续来分析一下lua中的协程如何与服务有机结合的,关于lua中协程的解释参见本文《lua中协程的理解》;
上一篇分析到,当一个lua服务收到消息后,在lua层,最终会执行到raw_dispatch_message函数,代码如下:
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)创建一个协程,该函数实现如下:
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
以上代码的中使用了弱表来作为协程池,关于lua中的弱表请参考《lua中的弱表理解》;
上述函数先通过table.remove(coroutine_pool)从coroutine_pool表中pop最后一个元素,即一个协程,
如果不存在协程,就会执行coroutine.create(function(...))来创建一个新的协程,协程执行函数接受可变参数,
注意:此时并未执行协程函数;
如果从协程库中取到了一个协程,直接调用了coroutine_resume(co, f),这个函数对应函数为:
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
", 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);
}