Pcall And Coroutines

lua-users home
wiki

pcall and coroutines didn't always get along. [coxpcall] for Lua 5.1 reimplements pcall/xpcall in terms of coroutines to allows things like yielding across a pcall. There were also patches like ResumableVmPatch. Lua 5.2 (LuaFiveTwo) implements an approach like ResumableVmPatch [1].

Below implements pcall/error like functions in terms of coroutines. The downside is the overhead of the coroutine construction per pcall. coxpcall is fairly similar but doesn't reimplement error as a 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
edit · history
Last edited July 4, 2010 12:27 am GMT (diff)