aboutsummaryrefslogblamecommitdiff
path: root/src/syslua/lx/mainloop.lua
blob: c2ae88d9d89b9fe73a6d65b8827bc20e0a291ce0 (plain) (tree)




















































































                                                                                              
local sys = require 'lx.sys'
local sysdef = require 'lx.sysdef'

local mainloop = {}

local fds = {}
local mainloop_must_exit = false
local mainloop_fds_change = false

function new_fd(fd, error_cb)
	local fd = {
		fd = fd,
		error_cb = error_cb,
		rd_expect = {},
		wr_buf = "",
	}
	function fd:write(str)
		-- TODO: try write immediately when possible
		self.wr_buf = self.wr_buf .. str
	end
	function fd:expect(len, cb)
		table.insert(self.rd_expect, {len, "", cb})
	end
end

function mainloop.add_fd(fd, error_cb)
	local fd = new_fd(fd, error_cb)
	table.insert(fds, fd)
	mainloop_fds_change = true
	return fd
end

function mainloop.run()
	mainloop_must_exit = false
	sel_fds = {}
	while not mainloop_must_exit do
		local deadlock = true
		for i, fd in pairs(fds) do
			if mainloop_fds_change then
				sel_fds[i] = {fd.fd}
			end
			local flags = sysdef.SEL_ERROR
			if fd.rd_expect[1] then
				flags = flags | sysdef.SEL_READ
				deadlock = false
			end
			if #fd.wr_buf > 0 then
				flags = flags | sysdef.SEL_WRITE
				deadlock = false
			end
			sel_fds[i][2] = flags
		end
		mainloop_fds_change = false
		assert(not deadlock, "Mainloop does nothing!")

		local res = sys.select(sel_fds, -1)
		assert(res, "select() call failed")
		for i, fd = pairs(fds) do
			local flags = sel_fds[i][3]
			if flags & sysdef.SEL_ERROR ~= 0 then
				fd.error_cb(fd)
			else
				if flags & sysdef.SEL_READ ~= 0 then
					local reader = fd.rd_expect[1]
					local tmp = sys.read(fd.fd, 0, reader[1] - #reader[2])
					reader[2] = reader[2] .. tmp
					if #reader[2] == reader[1] then
						reader[3](reader[2])
						table.remove(fd.rd_expect, 1)
					end
				end
				if flags & sysdef.SEL_WRITE ~= 0 then
					local r = sys.write(fd.fd, 0, fd.wr_buf)
					fd.wr_buf = fd.wr_buf:sub(r+1)
				end
			end
		end
	end
end

function mainloop.exit()
	mainloop_must_exit = true
end

return mainloop