local jobs = {}
local time = 0.0
local time_next = math.huge

core.register_globalstep(function(dtime)
	time = time + dtime

	if time < time_next then
		return
	end

	time_next = math.huge

	-- Iterate backwards so that we miss any new timers added by
	-- a timer callback.
	for i = #jobs, 1, -1 do
		local job = jobs[i]
		if time >= job.expire then
			core.set_last_run_mod(job.mod_origin)
			job.func(unpack(job.arg))
			local jobs_l = #jobs
			jobs[i] = jobs[jobs_l]
			jobs[jobs_l] = nil
		elseif job.expire < time_next then
			time_next = job.expire
		end
	end
end)

function core.after(after, func, ...)
	assert(tonumber(after) and type(func) == "function",
		"Invalid minetest.after invocation")
	local expire = time + after
	local new_job = {
		func = func,
		expire = expire,
		arg = {...},
		mod_origin = core.get_last_run_mod(),
	}

	jobs[#jobs + 1] = new_job
	time_next = math.min(time_next, expire)

	return {
		cancel = function()
			new_job.func = function() end
			new_job.args = {}
		end
	}
end