lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It can be done by wrap lua's coroutine as a table. and must rewrite coroutine's API.

--coroutine pool
local freelist={};

function get_freelist()
return freelist;
end

function find_free(f)
if #freelist > 0 then
local free = freelist[#freelist]
table.remove(freelist)
return {thread = free,func = f,status="create"}
end
end

local coroutine_create = coroutine.create
local coroutine_resume = coroutine.resume
local coroutine_status = coroutine.status
--local coroutine_running = coroutine.running

function create_co(f)
local tb = {func = f,status="create"}
tb.thread = coroutine_create(
function (...)
local arg = {...}
while true do
assert(type(arg[1]) == "function",arg[1])
arg[1](unpack(arg,2))
tb.status = "dead"
table.insert(freelist,coroutine.running())
arg = {coroutine.yield("co_end")}
end
end)
return tb
end

function coroutine.create(f)
return find_free(f) or create_co(f)
end

function coroutine.resume(co,...)
if type(co) == "thread" then
return coroutine_resume(co,...)
end
if co.status == "create" then
co.status = "running"
return coroutine_resume(co.thread,co.func,...)
elseif co.status == "running" then
return coroutine_resume(co.thread,...)
else --dead
print("error!!! co.status = "..co.status)
end
end

function coroutine.status(co)
if type(co) == "thread" then
return coroutine_status(co)
end
assert(type(co) == "table")
if co.status == "dead" or co.status == "create" then
return "dead"
end
return coroutine_status(co.thread)
end



2013/7/24 jiang yu <yu.jiang.163@gmail.com>
Hi !
  In my game server there are so many lua's coroutine to make thing easy -- such as instead of state machine. when profile it I find it cost too much memory and cause gc more often. So I want reuse Lua's Coroutine, according to lua gems http://www.lua.org/gems/sample.pdf: I write a file in 200 line(140 for test) to do it.(hope not brother you).
  the question is, the test can not pass the test_error_oldnew(), the situation is: test1 create a coroutine and run it, the pool find it end and give the coroutine to test2, but the test1 not realise it end and resume it again. 
  I know it is a bug that should not happen,but may be happen. in the normal lua case, it is not dangerous just get a error and easily detect, but under coroutine pool it is very dangerous and can not detect. how to deal with it?

--coroutine pool
local freelist={};

function get_freelist()
return freelist;
end

function find_free()
if #freelist > 0 then
local free = freelist[#freelist]
table.remove(freelist)
return free
end
end

CO_TAG = "CO" --for safe use CO_TAG

function create_co()
local co = coroutine.create(
function (...)
local arg = {...}
while true do
if arg[1] ~= CO_TAG then
--do nothing, but can not error(...) because the co is in freelist. but how to let caller know it?
print("error! arg[1] ~= CO_TAG",arg[1])
else
assert(type(arg[2]) == "function",arg[2])
arg[2](unpack(arg,3))
table.insert(freelist,coroutine.running())
end
arg = {coroutine.yield("co_end")}
end
end)
return co
end

function get_free_co()
return find_free() or create_co()
end

function wrap(f)
local free = get_free_co()
return function(...)
local isok, msg = coroutine.resume(free,CO_TAG,f,...)
if not isok then
print("CO.wrap error: ", msg)
end
return isok, msg
end
end

function run(f,...)
local free = get_free_co()
return coroutine.resume(free,CO_TAG,f,...)
end

function run_rt_co(f,...)
local free = get_free_co()
return free,coroutine.resume(free,CO_TAG,f,...)
end

--test
local test_co1
function t1(p1)
test_co1 = coroutine.running()
print("inco t1 1",p1)
local rt = coroutine.yield("t1 y1")
assert(rt == "r1")
print("inco t1 end",rt)
end

local test_co2
function t2(p1,p2)
test_co2 = coroutine.running()
print("inco t2 1",p1,p2)
local rt = coroutine.yield("t2 y2")
assert(rt == "r2")
print("inco t2 2",rt)
rt = coroutine.yield("t2 y2")
assert(rt == "r2")
print("inco t2 end",rt)
end


local test_co3
function t3(p1,p2)
test_co3 = coroutine.running()
print("inco t3 1",p1,p2)
local rt = coroutine.yield("t3 y3")
assert(rt == "r3")
error("inco t3")
rt = coroutine.yield("t3 y3")
assert(rt == "r3")
print("inco t3 end",rt)
end

function test()
print("#freelist",#freelist)
assert(#freelist == 0)
local ok,rt = run(t1,"p1")
print('run(t1,"p1")',ok,rt)
ok,rt = coroutine.resume(test_co1,"r1")
print('coroutine.resume(test_co1,"r1")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)

ok,rt = run(t2,"p1","p2")
assert(test_co1 == test_co2)
--print("freelist",#freelist)
print('run(t2,"p1","p2")',ok,rt)
ok,rt = coroutine.resume(test_co2,"r2")
print('coroutine.resume(test_co2,"r2")',ok,rt)
ok,rt = coroutine.resume(test_co2,"r2")
print('coroutine.resume(test_co2,"r2")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)
end

function test_wrap()
print("#freelist",#freelist)
assert(#freelist == 0)
local f1 = wrap(t1)
local ok,rt = f1("p1")
print('f1("p1")',ok,rt)
ok,rt = coroutine.resume(test_co1,"r1")
print('coroutine.resume(test_co1,"r1")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)

local f2 = wrap(t2)
ok,rt = f2("p1","p2")
assert(test_co1 == test_co2)
--print("freelist",#freelist)
print('f2("p1","p2")',ok,rt)
ok,rt = coroutine.resume(test_co2,"r2")
print('coroutine.resume(test_co2,"r2")',ok,rt)
ok,rt = coroutine.resume(test_co2,"r2")
print('coroutine.resume(test_co2,"r2")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)
end

function test_error()
print("#freelist",#freelist)
assert(#freelist == 0)
local f3 = wrap(t3)
print("local f3 = wrap(t3)")
ok,rt = f3("p1","p2")
print('f3("p1","p2")',ok,rt)
ok,rt = coroutine.resume(test_co3,"r3")
print('coroutine.resume(test_co3,"r3")',ok,rt)
ok,rt = coroutine.resume(test_co3,"r3")
print('coroutine.resume(test_co3,"r3")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 0)
end

function test_error_tag()
print("#freelist",#freelist)
assert(#freelist == 0)
local f1 = wrap(t1)
local ok,rt = f1("p1")
print('f1("p1")',ok,rt)
ok,rt = coroutine.resume(test_co1,"r1")
print('coroutine.resume(test_co1,"r1")',ok,rt)
assert(#freelist == 1)
ok,rt = coroutine.resume(test_co1,"r1") --CO_TAG will used here!
print('coroutine.resume(test_co1,"r1")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)
end

function test_error_oldnew()
print("#freelist",#freelist)
assert(#freelist == 0)
local f1 = wrap(t1)
local ok,rt = f1("p1")
print('f1("p1")',ok,rt)
ok,rt = coroutine.resume(test_co1,"r1")
print('coroutine.resume(test_co1,"r1")',ok,rt)
print("#freelist",#freelist)
assert(#freelist == 1)

local f2 = wrap(t2)
print("local f2 = wrap(t2)")
ok,rt = f2("p1","p2")
assert(test_co1 == test_co2)
print("freelist",#freelist)
assert(#freelist == 0)
print('f2("p1","p2")',ok,rt)
ok,rt = coroutine.resume(test_co2,"r2")
print('coroutine.resume(test_co2,"r2")',ok,rt)

ok,rt = coroutine.resume(test_co1,"r1")
print('coroutine.resume(test_co1,"r1")',ok,rt)
print("#freelist",#freelist)
end

test_error_oldnew()