lua-users home
lua-l archive

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


On 28 Jan 2013, at 20:12, Noah Watkins wrote:

> Our Lua functions look like:
> 
> function do_something(context)
>  … do stuff …
>  if (condition)
>    context.abort()
> end
> 
> We map context.abort() to lua_error, and all other return paths are an implicit commit/success. We'd like to provide an explicit context.commit() function that never returns, but doesn't necessarily use the lua_error mechanism.

I was playing with something like this the other day. I think it's correct, but I'm still getting to grips with coroutines, so it might not be.

Have a look at the attached code for the transaction function (Lua 5.2 only). You should lua_pcall it, with the function you actually want to call as the first argument. It will return false/true if context.abort() or context.commit() were called. Errors should be handled as normal. The function can yield and will be resumed. There's no support for returning values - although I think this is pretty easy to add. Also, a failure to commit/abort results in an error.

Kev

-----------------------------

function transaction(f, ...)
	local args = {...}
	local uniqueval = {}

	local coroutine = require("coroutine")
	local table = require("table")
	
	local cmds = {}
	function cmds.abort()
		coroutine.yield(uniqueval, false)
	end
	function cmds.commit()
		coroutine.yield(uniqueval, true)
	end

	local co = coroutine.create(function()
		local ok, err = pcall(f, cmds, table.unpack(args))
		if ok then err = "You forgot to commit" end
		error(err)
	end)

	::restart_routine::
	local yv = {coroutine.resume(co)}
	if yv[1] == false then
		--There was an error, pass it on
		error(yv[2]);
	end

	--There was no error, it must be a yield
	assert(coroutine.status(co) == "suspended");

	if yv[2] ~= uniqueval then
		--It was another yield
		local newvals = coroutine.yield(table.unpack(yv, 2))
		goto restart_routine
	end

	assert(type(yv[3]) == "boolean")
	return yv[3]
end