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
|
/*-------------------------------------------------------------------------*\
* Marks type as selectable
* Input
* name: type name
\*-------------------------------------------------------------------------*/
void slct_addclass(lua_State *L, cchar *lsclass)
{
lua_pushstring(L, "selectable sockets");
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushstring(L, lsclass);
lua_pushnumber(L, 1);
lua_settable(L, -3);
lua_pop(L, 2);
}
/*-------------------------------------------------------------------------*\
* Gets a pointer to a socket structure from a userdata
* Input
* pos: userdata stack index
* Returns
* pointer to structure, or NULL if invalid type
\*-------------------------------------------------------------------------*/
static p_sock ls_toselectable(lua_State *L)
{
lua_getregistry(L);
lua_pushstring(L, "sock(selectable)");
lua_gettable(L, -2);
lua_pushstring(L, lua_type(L, -3));
lua_gettable(L, -2);
if (lua_isnil(L, -1)) {
lua_pop(L, 3);
return NULL;
} else {
lua_pop(L, 3);
return (p_sock) lua_touserdata(L, -1);
}
}
/*-------------------------------------------------------------------------*\
* Waits for a set of sockets until a condition is met or timeout.
* Lua Input: {input}, {output} [, timeout]
* {input}: table of sockets to be tested for input
* {output}: table of sockets to be tested for output
* timeout: maximum amount of time to wait for condition, in seconds
* Lua Returns: {input}, {output}, err
* {input}: table with sockets ready for input
* {output}: table with sockets ready for output
* err: "timeout" or nil
\*-------------------------------------------------------------------------*/
int global_select(lua_State *L)
{
int ms = lua_isnil(L, 3) ? -1 : (int) (luaL_opt_number(L, 3, -1) * 1000);
fd_set rfds, *prfds = NULL, wfds, *pwfds = NULL;
struct timeval tv, *ptv = NULL;
unsigned max = 0;
int byfds, readable, writable;
int toread = 1, towrite = 2;
lua_newtable(L); byfds = lua_gettop(L); /* sockets indexed by descriptor */
lua_newtable(L); readable = lua_gettop(L);
lua_newtable(L); writable = lua_gettop(L);
/* collect sockets to be tested into FD_SET structures and fill byfds */
if (lua_istable(L, toread))
prfds = tab2rfds(L, toread, &rfds, &max, byfds, readable, &ms);
else if (!lua_isnil(L, toread))
luaL_argerror(L, toread, "expected table or nil");
if (lua_istable(L, towrite))
pwfds = tab2wfds(L, towrite, &wfds, &max, byfds);
else if (!lua_isnil(L, towrite))
luaL_argerror(L, towrite, "expected table or nil");
/* fill timeval structure */
if (ms >= 0) {
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
ptv = &tv;
} else ptv = NULL; /* ptv == NULL when we don't have timeout */
/* see if we can read, write or if we timedout */
if (select(max+1, prfds, pwfds, NULL, ptv) <= 0 && ms >= 0) {
ls_pusherror(L, LS_TIMEOUT);
return 3;
}
/* collect readable and writable sockets into result tables */
fds2tab(L, prfds, max+1, byfds, readable);
fds2tab(L, pwfds, max+1, byfds, writable);
lua_pushnil(L);
return 3;
}
/*=========================================================================*\
* Select auxiliar functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Converts a FD_SET structure into a socket table set
* Input
* fds: pointer to FD_SET structure
* max: 1 plus the largest descriptor value in FD_SET
* byfds: table indexed by descriptor number, with corresponding socket tables
* can: table to receive corresponding socket table set
\*-------------------------------------------------------------------------*/
static void fds2tab(lua_State *L, fd_set *fds, int max, int byfds, int can)
{
int s;
if (!fds) return;
for (s = 0; s < max; s++) {
if (FD_ISSET(s, fds)) {
lua_pushnumber(L, lua_getn(L, can) + 1);
lua_pushnumber(L, s);
lua_gettable(L, byfds);
lua_settable(L, can);
}
}
}
/*-------------------------------------------------------------------------*\
* Converts a socket table set ito a FD_SET structure
* Input
* towrite: socket table set
* Output
* wfds: pointer to FD_SET structure to be filled
* max: largest descriptor value found in wfds
* byfds: table indexed by descriptor number, with corresponding socket tables
\*-------------------------------------------------------------------------*/
static fd_set *tab2wfds(lua_State *L, int towrite, fd_set *wfds,
int *max, int byfds)
{
int empty = 1;
FD_ZERO(wfds);
lua_pushnil(L);
while (lua_next(L, towrite)) {
p_sock sock = ls_toselectable(L);
if (sock) { /* skip strange fields */
NET_FD s = sock->fd;
if (s != NET_INVALIDFD) { /* skip closed sockets */
lua_pushnumber(L, s);
lua_pushvalue(L, -2);
lua_settable(L, byfds);
if (s > *max) *max = s;
FD_SET(s, wfds);
empty = 0;
}
}
/* get rid of value and expose index */
lua_pop(L, 1);
}
if (empty) return NULL;
else return wfds;
}
/*-------------------------------------------------------------------------*\
* Converts a socket table set ito a FD_SET structure
* Input
* toread: socket table set
* Output
* rfds: pointer to FD_SET structure to be filled
* max: largest descriptor value found in rfds
* byfds: table indexed by descriptor number, with corresponding socket tables
* readable: table to receive socket table if socket is obviously readable
* ms: will be zeroed if a readable socket is detected
\*-------------------------------------------------------------------------*/
static fd_set *tab2rfds(lua_State *L, int toread, fd_set *rfds,
int *max, int byfds, int readable, int *ms)
{
int empty = 1;
FD_ZERO(rfds);
lua_pushnil(L);
while (lua_next(L, toread)) {
p_sock sock = ls_toselectable(L);
if (sock) { /* skip strange fields */
NET_FD s = sock->fd;
if (s != NET_INVALID) { /* skip closed sockets */
/* a socket can have unread data in our internal buffer. we
pass them straight to the readable set, and test only to
find out which of the other sockets can be written to or
read from immediately. */
if (sock->vt->readable(sock)) {
*ms = 0;
lua_pushnumber(L, lua_getn(L, readable) + 1);
lua_pushvalue(L, -2);
lua_settable(L, readable);
} else {
lua_pushnumber(L, s);
lua_pushvalue(L, -2);
lua_settable(L, byfds);
if (s > *max) *max = s;
FD_SET(s, rfds);
empty = 0;
}
}
}
/* get rid of value and exposed index */
lua_pop(L, 1);
}
if (empty) return NULL;
else return rfds;
}
|