接上一篇分析《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);}