-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathasio.lua
More file actions
334 lines (280 loc) · 8.1 KB
/
Copy pathasio.lua
File metadata and controls
334 lines (280 loc) · 8.1 KB
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
-- Copyright (C) by Jianhao Zhang (heeroz)
local co_create = coroutine.create
local co_status = coroutine.status
local yield = coroutine.yield
local resume = coroutine.resume
local running = coroutine.running
local setmetatable = setmetatable
local type = type
local tostring = tostring
local assert = assert
local ok, new_table = pcall(require, "table.new")
if not ok then new_table = function() return {} end end
------------------ffi------------------------
local ok, ffi = pcall(require, "ffi")
assert(ok, 'need use luajit yet')
local ok, asio_c = pcall(ffi.load, 'asio')
if not ok then ok, asio_c = pcall(ffi.load, './libasio.so') end
if not ok then _, asio_c = pcall(ffi.load, 'asio.dll') end
assert(asio_c, 'load c lib failed.')
local C = ffi.C
ffi.cdef[[
typedef struct event_message_for_ffi {
char type;
int dest_id;
void* source;
const char* data;
size_t data_len;
} event_message;
event_message* asio_get(int wait_sec);
bool asio_stopped();
void asio_sleep(int dest_id, double sec);
void* asio_new_connect(const char* host, unsigned short port,
int dest_id, bool v6);
void* asio_new_connect_sockaddr(const char* p, int dest_id);
void asio_delete_connection(void* p);
void asio_conn_read(void* p, size_t size, int dest_id);
void asio_conn_read_some(void* p, int dest_id);
void asio_conn_write(void* p, const char* data, size_t size,
int dest_id);
void asio_conn_close(void* p);
void* asio_get_original_dst(void* p);
const char* asio_addr_to_str(const char* p);
void* asio_new_server(const char* ip, int port);
void asio_delete_server(void* p);
]]
------------------thread------------------------
local _M = {}
local th_tbl = new_table(100, 0)
local th_free_id = new_table(100, 0)
local th_to_id = new_table(0, 100)
function _M._get_tid(th)
return th_to_id[th]
end
function _M._get_free_tid()
local last_free = #th_free_id
local tid
if last_free == 0 then
tid = #th_tbl + 1
else
tid = th_free_id[last_free]
end
return tid, last_free
end
function _M._use_tid(tid, last_free, th)
if last_free > 0 then
th_free_id[last_free] = nil
end
th_tbl[tid] = th
th_to_id[th] = tid
end
function _M._remove_th(tid)
if not th_tbl[tid] then return end
-- local is_thtbl_array = #th_free_id == 0
-- local add_free = not is_thtbl_array or tid != (#th_tbl - 1)
local th = th_tbl[tid]
th_to_id[th] = nil
th_tbl[tid] = nil
--if add_free then
th_free_id[#th_free_id + 1] = tid
--end
end
local function _light_thread(tid, func, ...)
func(...)
_M._remove_th(tid)
end
local function _create_th()
local tid, useid = _M._get_free_tid()
-- todo: thread pool assign here
local th = co_create(_light_thread)
_M._use_tid(tid, useid, th)
return tid, th
end
function _M.spawn_light_thread(func, ...)
local tid, th = _create_th()
local ok, err = resume(th, tid, func, ...)
if not ok then
print( debug.traceback( th, err ))
end
return th
end
------------------connection------------------------
local conn_M = {}
conn_M.__index = conn_M
local sockaddr_size = 128
function conn_M:get_original_dst(data)
local addr = asio_c.asio_get_original_dst(self.cpoint)
if addr == nil then return nil end
return ffi.string(addr, sockaddr_size)
end
function conn_M:read(n)
local th = running()
assert(th, 'need be called in light thread.')
asio_c.asio_conn_read(self.cpoint, n, th_to_id[th])
local ok, data = yield()
if ok then
return data
else
return nil, data
end
end
function conn_M:read_some()
local th = running()
assert(th, 'need be called in light thread.')
asio_c.asio_conn_read_some(self.cpoint, th_to_id[th])
local ok, data = yield()
if ok then
return data
else
local ok, err = yield()
return data, err
end
end
function conn_M:write(data)
assert(data and #data > 0)
local th = running()
assert(th, 'need be called in light thread.')
asio_c.asio_conn_write(self.cpoint, data, #data, th_to_id[th])
local ok, err_msg = yield()
if ok then
return true
else
return nil, err_msg
end
end
function conn_M:close()
asio_c.asio_conn_close(self.cpoint)
self.cpoint = nil
setmetatable(self, nil)
self.read = function() return nil, 'Already closed.' end
self.read_some = self.read
self.write = self.read
self.close = function() end
end
------------------udp------------------------
-- local udp_M = {}
-- udp_M.__index = udp_M
-- function _M.udp(host, port)
-- if type(port) == 'string' then
-- port = tonumber(port)
-- end
-- local th = running()
-- assert(th, 'need be called in light thread.')
-- local cpoint
-- if port == nil and #host >= 64 then
-- cpoint = asio_c.asio_new_udp_sockaddr(host, th_to_id[th])
-- else
-- cpoint = asio_c.asio_new_udp(host, port, th_to_id[th])
-- end
-- local udp = {
-- cpoint = ffi.gc(cpoint, asio_c.asio_delete_udp),
-- }
-- setmetatable(udp, udp_M)
-- return udp
-- end
-- function _M:receive()
-- local th = running()
-- assert(th, 'need be called in light thread.')
-- asio_c.asio_udp_receive(self.cpoint, n, th_to_id[th])
-- local ok, data = yield()
-- if ok then
-- return data
-- else
-- return nil, data
-- end
-- end
-- function _M:send(data)
-- assert(data and #data > 0)
-- local th = running()
-- assert(th, 'need be called in light thread.')
-- asio_c.asio_conn_write(self.cpoint, data, #data, th_to_id[th])
-- local ok, err_msg = yield()
-- if ok then
-- return true
-- else
-- return nil, err_msg
-- end
-- end
------------------asio------------------------
local EVT_ACCEPT = 1
local EVT_CONTINUE = 2
local handler_tbl = {}
local function _make_connection(cpoint)
local con = {
cpoint = ffi.gc(cpoint, asio_c.asio_delete_connection),
}
setmetatable(con, conn_M)
return con
end
local function _evt_disp(evt)
if evt.type == EVT_ACCEPT then
local handler = handler_tbl[evt.dest_id]
local con = _make_connection(evt.source)
handler(con)
elseif evt.type == EVT_CONTINUE then
local th = th_tbl[evt.dest_id]
local source = evt.source ~= nil and evt.source or nil
local data = ffi.string(evt.data, evt.data_len)
local ok, err = resume(th, source, data)
if not ok then
print( debug.traceback( th, err ))
end
end
end
function _M.connect(host, port, resolve_v6)
if type(port) == 'string' then
port = tonumber(port)
end
local th = running()
assert(th, 'need be called in light thread.')
local cpoint
if port == nil and #host >= 64 then
cpoint = asio_c.asio_new_connect_sockaddr(host, th_to_id[th])
else
cpoint = asio_c.asio_new_connect(host, port, th_to_id[th],
resolve_v6 and true or false)
end
local con = _make_connection(cpoint)
local ok, msg = yield()
if ok == nil then return nil, msg end
return con
end
function _M.addr_to_str(addr)
assert(#addr >= 64)
return ffi.string(asio_c.asio_addr_to_str(addr))
end
function _M.server(ip, port, accept_handler)
handler_tbl[port] = accept_handler
local sv = asio_c.asio_new_server(ip, port)
if sv == nil then
return nil
else
return ffi.gc(sv, asio_c.asio_delete_server)
end
end
function _M.destory_server(server_holder)
asio_c.asio_delete_server(ffi.gc(server_holder, nil))
end
function _M.sleep(sec)
local th = running()
assert(th, 'need be called in light thread.')
asio_c.asio_sleep(th_to_id[th], sec)
yield()
return
end
function _M.run()
while true do
local evt = asio_c.asio_get(-1)
if evt ~= nil then
_evt_disp(evt)
end
if asio_c.asio_stopped() then break end
end
end
function _M.run_once(wait_sec)
local evt = asio_c.asio_get(wait_sec or -1)
if evt ~= nil then
_evt_disp(evt)
end
end
return _M