aboutsummaryrefslogtreecommitdiff
path: root/src/syslua/lx/mainloop.lua
blob: b1b3feb8bab8065c9f7e78dc7b06e002af96b2d8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
	return fd
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 in 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