-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathlib_poll.lua
311 lines (231 loc) · 7.45 KB
/
lib_poll.lua
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
-- lib_poll.lua
module(..., package.seeall)
-- partly copied from: https://github.com/chatid/fend/blob/master/poll.lua
local ffi = require "ffi"
local C = ffi.C
local socket = require "lib_socket"
local bit = require "bit"
local band = bit.band
local bor = bit.bor
ffi.cdef[[
void* calloc (size_t num, size_t size);
void* realloc (void* ptr, size_t size);
void free (void* ptr);
]]
local poll_event, in_callback, out_callback, close_callback, error_callback
local fds, nfds, pollCount
local fdsListSize, timeout, fdsListAddCount, debug_level
local fdAddCount, fdRemoveCount
local function clear_all()
poll_event = nil
in_callback = nil -- runs this function when data has come in
out_callback = nil -- runs this function when you can write out
close_callback = nil -- runs this function when you need to close socket
error_callback = nil -- runs this function when error has happened
fds = nil -- ffi.C memory area containing all (max. fdsListSize) "struct pollfd":s
nfds = 0 -- number of active fds
pollCount = 0
fdsListSize = 0 -- how many fd's can fit in to fds memory size
timeout = 0
fdsListAddCount = 10
debug_level = 0
fdAddCount = 0
fdRemoveCount = 0
end
clear_all()
local function expand_fds(oldFds, countFds)
print("poll.expand_fds: ", oldFds, countFds)
local newFds
if oldFds then
ffi.gc(oldFds , nil)
-- oldFds will be used in realloc, mut not garbage collect it, remove it's gc function
end
newFds = C.realloc(oldFds, ffi.sizeof("struct pollfd") * countFds)
if newFds == nil then
error("Cannot re-allocate memory (poll.expand_fds)")
end
local ret = ffi.cast("struct pollfd*", newFds)
return ffi.gc(ret, ffi.C.free) -- assign ffi.C.free for garbage collect
end
local function fd_arr_index(fd)
local idx = nfds - 1 -- better to loop from en, more likely to find correct
while idx >= 0 do
if fds[idx].fd == fd then return idx end
idx = idx - 1
end
return -1
end
local function fd_arr_show()
local idx = 0
local txt = "fds["
while idx < nfds do
txt = txt..fds[idx].fd
idx = idx + 1
if idx < nfds then
txt = txt..", "
end
end
return txt.."], nfds="..nfds
end
function add_fd(fd, events)
fdAddCount = fdAddCount + 1
local idx = fd_arr_index(fd)
if idx >= 0 then
-- is old fd number, is ok when we reuse addresses ???
print("ERR: Fd was already added to array (poll.add_fd): idx="..idx..", fd="..fd..", nfds="..nfds)
print(fd_arr_show())
else
if nfds >= fdsListSize then -- expand nfds C memory area
fdsListSize = fdsListSize + fdsListAddCount
fds = expand_fds(fds, fdsListSize)
end
end
fds[nfds].fd = fd -- set C struct pollfd field fd, same as fds[nfds].fd = fd
fds[nfds].events = events -- bor(C.POLLIN, C.POLLOUT, C.POLLRDHUP)
-- fds[nfds].revents = 0 -- no need to set
nfds = nfds + 1 -- fds is C-mem area and 0-based, so add it only in the end
if debug_level > 0 then print(" poll.add_fd: fd="..fd..", nfds="..nfds) end
end
function remove_fd(fd)
fdRemoveCount = fdRemoveCount + 1
local idx = fd_arr_index(fd)
if idx < 0 then
error("ERR: Fd was not found from array (poll.remove_fd): fd="..fd..", nfds="..nfds)
print(fd_arr_show())
return
end
if idx ~= nfds-1 then -- if not last item, move an item from end of list to fill the empty spot
if debug_level > 0 then
print(" poll.remove_fd from middle: idx=".. idx+1 ..", fd="..fd..", nfds="..nfds)
print(" "..fd_arr_show())
end
nfds = nfds - 1 -- decrease nfds count so that fds[nfds] is zero-based
local lastfd = fds[nfds].fd
local lastevent = fds[nfds].events
fds[idx].fd = lastfd
fds[idx].events = lastevent
if debug_level > 0 then
print(" "..fd_arr_show())
end
else
nfds = nfds - 1 -- decrease nfds count so that fds[nfds] is zero-based
fds[idx].fd = -1
if debug_level > 0 then
print(" poll.remove_fd from end : idx=".. idx+1 ..", fd="..fd..", nfds="..nfds)
print(" "..fd_arr_show())
end
end
end
function remove_all(close_func)
for i=0,nfds-1 do
--print("poll.remove_all: ", fds[i].fd)
close_func(fds[i].fd)
end
clear_all()
end
function poll_count()
return pollCount
end
function fd_count()
return nfds
end
function fd_add_count()
return fdAddCount
end
function fd_remove_count()
return fdRemoveCount
end
function timeout_set(timeOut)
timeout = timeOut
end
function in_callback_set(func)
in_callback = func
end
function out_callback_set(func)
out_callback = func
end
function close_callback_set(func)
close_callback = func
end
function error_callback_set(func)
error_callback = func
end
-- http://www.greenend.org.uk/rjk/tech/poll.html
-- *** we use elseif here because id some event is really needed it will come on next poll *** --
local function poll_event_nodebug(evt, fd)
if band(evt, C.POLLHUP) ~= 0 then
close_callback(fd)
elseif band(evt, C.POLLIN) ~= 0 then
in_callback(fd)
elseif band(evt, C.POLLOUT) ~= 0 then
out_callback(fd)
elseif band(evt, C.POLLNVAL) ~= 0 then
error_callback(fd, "POLLNVAL")
elseif band(evt, C.POLLERR) ~= 0 then
error_callback(fd, "POLLERR")
end
end
local function poll_event_debug(evt, fd, i)
-- debug_level version, must be same ifs as above
if band(evt, C.POLLHUP) ~= 0 then -- usually C.POLLIN | C.POLLHUP, but we don't want C.POLLIN
-- POLLHUP, output only
print("\n"..pollCount..". POLLHUP : idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds)
close_callback(fd)
elseif band(evt, C.POLLIN) ~= 0 then
-- POLLIN
print("\n"..pollCount..". POLLIN : idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds)
in_callback(fd)
--[[elseif band(evt, C.POLLPRI) ~= 0 then
--[ [POLLPRI Priority data may be read without blocking. This flag is not supported by the Microsoft Winsock provider.] ]
if debug_level > 0 then print(pollCount..". POLLPRI : idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds) end
in_callback(fd)]]
elseif band(evt, C.POLLOUT) ~= 0 then
-- POLLOUT
-- because we exclude POLLHUP and POLLIN we cand exclude POLLOUT
print("\n"..pollCount..". POLLOUT : idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds)
out_callback(fd)
elseif band(evt, C.POLLNVAL) ~= 0 then
-- POLLNVAL, output only
print("\n"..pollCount..". POLLNVAL: idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds)
error_callback(fd, "POLLNVAL")
elseif band(evt, C.POLLERR) ~= 0 then
-- POLLERR, output only
print("\n"..pollCount..". POLLERR : idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds)
error_callback(fd, "POLLERR")
end
--[[elseif band(evt, C.POLLRDHUP) ~= 0 then
-- POLLRDHUP
if debug_level > 0 then print(pollCount..". POLLRDHUP: idx="..i..", evt="..evt..", fd="..fd..", nfds="..nfds) end
close_callback(fd)
end]]
end
function debug_level_set(level)
debug_level = level
if debug_level > 0 then
poll_event = poll_event_debug -- change to debug function
else
poll_event = poll_event_nodebug -- change to debug function
end
print("poll debug level: "..debug_level, poll_event)
end
function poll()
pollCount = pollCount + 1
local ret = socket.poll(fds, nfds, timeout)
if ret == -1 then
print(pollCount..". poll, nfds="..nfds)
socket.cleanup(fds[0].fd, ret, "socket.poll failed with error: ")
elseif ret == 0 then
return 0
end
-- loop all events, break loop as soon as possible
local served = 0
for i=1,nfds do
local evt = fds[i-1].revents
if evt~= 0 then
local fd = fds[i-1].fd
poll_event(evt, fd, i)
end
if served == ret then break end -- break loop as soon as possible
end
return ret
end