Pcall 与协程

lua-users home
wiki

pcall 和协程并非总是和谐共处。Lua 5.1 的 [coxpcall] 使用协程重新实现了 pcall/xpcall,从而允许跨 pcall 进行 yield。此外,还有像 ResumableVmPatch 这样的补丁。Lua 5.2 (LuaFiveTwo) 实现了一个类似于 ResumableVmPatch 的方法 [1]

下面是使用协程实现的类似 pcall/error 的函数。缺点是每次 pcall 都会有协程构建的开销。coxpcall 与此类似,但它没有将 error 重实现为 coroutine.yield

local function tuple(...)
  return {n=select('#', ...), ...}
end

function pcall(f, ...)
  local co = coroutine.create(f)
  local res = tuple(coroutine.resume(co, ...))
  if res[1] and coroutine.status(co) == "suspended" then
    res[1] = false
  end
  return unpack(res, 1, res.n)
end

local handlers = setmetatable({}, {__mode='kv'})

function xpcall(f, err, ...)
  local co = coroutine.create(f)
  handlers[co] = err
  local res = tuple(coroutine.resume(co, ...))
  if res[1] and coroutine.status(co) == "suspended" then
    res[1] = false
  end
  if not res[1] and err then
    res[2] = err(co, res[2])
      -- note: assumes err can accept coroutine as first argument.
  end
  return unpack(res, 1, res.n)
end


function error(e, level_) --FIX:level not handled
  coroutine.yield(e)
end


-- test

local function g(x)
  if x == "fail" then
    error "fail"
  elseif x == "fail2" then
    local y = nil + nil  -- error raised by Lua
  end
end

local function f(x)
  print(1)
  g(x)
  print(2)
  return 3,4
end

print(pcall(f, "ok"))
print(pcall(f, "fail"))
print(pcall(f, "fail2"))
print(xpcall(f, debug.traceback, "fail"))
print(xpcall(f, debug.traceback, "fail2"))

--DavidManura


RecentChanges · preferences
编辑 · 历史
最后编辑于 2010 年 7 月 3 日下午 6:27 GMT (差异)