diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2002-03-22 20:07:43 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2002-03-22 20:07:43 +0000 |
commit | 3bd99ec59996653011f0eb40a0be4a523fe82897 (patch) | |
tree | 71030ee34cbc705517111fe8c6e4a459a6defe20 /src | |
parent | e2d21b237d0457fde60c1803ee4153ef7238e0eb (diff) | |
download | luasocket-3bd99ec59996653011f0eb40a0be4a523fe82897.tar.gz luasocket-3bd99ec59996653011f0eb40a0be4a523fe82897.tar.bz2 luasocket-3bd99ec59996653011f0eb40a0be4a523fe82897.zip |
Initial revision
Diffstat (limited to 'src')
-rw-r--r-- | src/select.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/select.c b/src/select.c new file mode 100644 index 0000000..1f83b7a --- /dev/null +++ b/src/select.c | |||
@@ -0,0 +1,194 @@ | |||
1 | /*-------------------------------------------------------------------------*\ | ||
2 | * Marks type as selectable | ||
3 | * Input | ||
4 | * name: type name | ||
5 | \*-------------------------------------------------------------------------*/ | ||
6 | void slct_addclass(lua_State *L, cchar *lsclass) | ||
7 | { | ||
8 | lua_pushstring(L, "selectable sockets"); | ||
9 | lua_gettable(L, LUA_REGISTRYINDEX); | ||
10 | lua_pushstring(L, lsclass); | ||
11 | lua_pushnumber(L, 1); | ||
12 | lua_settable(L, -3); | ||
13 | lua_pop(L, 2); | ||
14 | } | ||
15 | |||
16 | /*-------------------------------------------------------------------------*\ | ||
17 | * Gets a pointer to a socket structure from a userdata | ||
18 | * Input | ||
19 | * pos: userdata stack index | ||
20 | * Returns | ||
21 | * pointer to structure, or NULL if invalid type | ||
22 | \*-------------------------------------------------------------------------*/ | ||
23 | static p_sock ls_toselectable(lua_State *L) | ||
24 | { | ||
25 | lua_getregistry(L); | ||
26 | lua_pushstring(L, "sock(selectable)"); | ||
27 | lua_gettable(L, -2); | ||
28 | lua_pushstring(L, lua_type(L, -3)); | ||
29 | lua_gettable(L, -2); | ||
30 | if (lua_isnil(L, -1)) { | ||
31 | lua_pop(L, 3); | ||
32 | return NULL; | ||
33 | } else { | ||
34 | lua_pop(L, 3); | ||
35 | return (p_sock) lua_touserdata(L, -1); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | /*-------------------------------------------------------------------------*\ | ||
40 | * Waits for a set of sockets until a condition is met or timeout. | ||
41 | * Lua Input: {input}, {output} [, timeout] | ||
42 | * {input}: table of sockets to be tested for input | ||
43 | * {output}: table of sockets to be tested for output | ||
44 | * timeout: maximum amount of time to wait for condition, in seconds | ||
45 | * Lua Returns: {input}, {output}, err | ||
46 | * {input}: table with sockets ready for input | ||
47 | * {output}: table with sockets ready for output | ||
48 | * err: "timeout" or nil | ||
49 | \*-------------------------------------------------------------------------*/ | ||
50 | int global_select(lua_State *L) | ||
51 | { | ||
52 | int ms = lua_isnil(L, 3) ? -1 : (int) (luaL_opt_number(L, 3, -1) * 1000); | ||
53 | fd_set rfds, *prfds = NULL, wfds, *pwfds = NULL; | ||
54 | struct timeval tv, *ptv = NULL; | ||
55 | unsigned max = 0; | ||
56 | int byfds, readable, writable; | ||
57 | int toread = 1, towrite = 2; | ||
58 | lua_newtable(L); byfds = lua_gettop(L); /* sockets indexed by descriptor */ | ||
59 | lua_newtable(L); readable = lua_gettop(L); | ||
60 | lua_newtable(L); writable = lua_gettop(L); | ||
61 | /* collect sockets to be tested into FD_SET structures and fill byfds */ | ||
62 | if (lua_istable(L, toread)) | ||
63 | prfds = tab2rfds(L, toread, &rfds, &max, byfds, readable, &ms); | ||
64 | else if (!lua_isnil(L, toread)) | ||
65 | luaL_argerror(L, toread, "expected table or nil"); | ||
66 | if (lua_istable(L, towrite)) | ||
67 | pwfds = tab2wfds(L, towrite, &wfds, &max, byfds); | ||
68 | else if (!lua_isnil(L, towrite)) | ||
69 | luaL_argerror(L, towrite, "expected table or nil"); | ||
70 | /* fill timeval structure */ | ||
71 | if (ms >= 0) { | ||
72 | tv.tv_sec = ms / 1000; | ||
73 | tv.tv_usec = (ms % 1000) * 1000; | ||
74 | ptv = &tv; | ||
75 | } else ptv = NULL; /* ptv == NULL when we don't have timeout */ | ||
76 | /* see if we can read, write or if we timedout */ | ||
77 | if (select(max+1, prfds, pwfds, NULL, ptv) <= 0 && ms >= 0) { | ||
78 | ls_pusherror(L, LS_TIMEOUT); | ||
79 | return 3; | ||
80 | } | ||
81 | /* collect readable and writable sockets into result tables */ | ||
82 | fds2tab(L, prfds, max+1, byfds, readable); | ||
83 | fds2tab(L, pwfds, max+1, byfds, writable); | ||
84 | lua_pushnil(L); | ||
85 | return 3; | ||
86 | } | ||
87 | |||
88 | /*=========================================================================*\ | ||
89 | * Select auxiliar functions | ||
90 | \*=========================================================================*/ | ||
91 | /*-------------------------------------------------------------------------*\ | ||
92 | * Converts a FD_SET structure into a socket table set | ||
93 | * Input | ||
94 | * fds: pointer to FD_SET structure | ||
95 | * max: 1 plus the largest descriptor value in FD_SET | ||
96 | * byfds: table indexed by descriptor number, with corresponding socket tables | ||
97 | * can: table to receive corresponding socket table set | ||
98 | \*-------------------------------------------------------------------------*/ | ||
99 | static void fds2tab(lua_State *L, fd_set *fds, int max, int byfds, int can) | ||
100 | { | ||
101 | int s; | ||
102 | if (!fds) return; | ||
103 | for (s = 0; s < max; s++) { | ||
104 | if (FD_ISSET(s, fds)) { | ||
105 | lua_pushnumber(L, lua_getn(L, can) + 1); | ||
106 | lua_pushnumber(L, s); | ||
107 | lua_gettable(L, byfds); | ||
108 | lua_settable(L, can); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /*-------------------------------------------------------------------------*\ | ||
114 | * Converts a socket table set ito a FD_SET structure | ||
115 | * Input | ||
116 | * towrite: socket table set | ||
117 | * Output | ||
118 | * wfds: pointer to FD_SET structure to be filled | ||
119 | * max: largest descriptor value found in wfds | ||
120 | * byfds: table indexed by descriptor number, with corresponding socket tables | ||
121 | \*-------------------------------------------------------------------------*/ | ||
122 | static fd_set *tab2wfds(lua_State *L, int towrite, fd_set *wfds, | ||
123 | int *max, int byfds) | ||
124 | { | ||
125 | int empty = 1; | ||
126 | FD_ZERO(wfds); | ||
127 | lua_pushnil(L); | ||
128 | while (lua_next(L, towrite)) { | ||
129 | p_sock sock = ls_toselectable(L); | ||
130 | if (sock) { /* skip strange fields */ | ||
131 | NET_FD s = sock->fd; | ||
132 | if (s != NET_INVALIDFD) { /* skip closed sockets */ | ||
133 | lua_pushnumber(L, s); | ||
134 | lua_pushvalue(L, -2); | ||
135 | lua_settable(L, byfds); | ||
136 | if (s > *max) *max = s; | ||
137 | FD_SET(s, wfds); | ||
138 | empty = 0; | ||
139 | } | ||
140 | } | ||
141 | /* get rid of value and expose index */ | ||
142 | lua_pop(L, 1); | ||
143 | } | ||
144 | if (empty) return NULL; | ||
145 | else return wfds; | ||
146 | } | ||
147 | |||
148 | /*-------------------------------------------------------------------------*\ | ||
149 | * Converts a socket table set ito a FD_SET structure | ||
150 | * Input | ||
151 | * toread: socket table set | ||
152 | * Output | ||
153 | * rfds: pointer to FD_SET structure to be filled | ||
154 | * max: largest descriptor value found in rfds | ||
155 | * byfds: table indexed by descriptor number, with corresponding socket tables | ||
156 | * readable: table to receive socket table if socket is obviously readable | ||
157 | * ms: will be zeroed if a readable socket is detected | ||
158 | \*-------------------------------------------------------------------------*/ | ||
159 | static fd_set *tab2rfds(lua_State *L, int toread, fd_set *rfds, | ||
160 | int *max, int byfds, int readable, int *ms) | ||
161 | { | ||
162 | int empty = 1; | ||
163 | FD_ZERO(rfds); | ||
164 | lua_pushnil(L); | ||
165 | while (lua_next(L, toread)) { | ||
166 | p_sock sock = ls_toselectable(L); | ||
167 | if (sock) { /* skip strange fields */ | ||
168 | NET_FD s = sock->fd; | ||
169 | if (s != NET_INVALID) { /* skip closed sockets */ | ||
170 | /* a socket can have unread data in our internal buffer. we | ||
171 | pass them straight to the readable set, and test only to | ||
172 | find out which of the other sockets can be written to or | ||
173 | read from immediately. */ | ||
174 | if (sock->vt->readable(sock)) { | ||
175 | *ms = 0; | ||
176 | lua_pushnumber(L, lua_getn(L, readable) + 1); | ||
177 | lua_pushvalue(L, -2); | ||
178 | lua_settable(L, readable); | ||
179 | } else { | ||
180 | lua_pushnumber(L, s); | ||
181 | lua_pushvalue(L, -2); | ||
182 | lua_settable(L, byfds); | ||
183 | if (s > *max) *max = s; | ||
184 | FD_SET(s, rfds); | ||
185 | empty = 0; | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | /* get rid of value and exposed index */ | ||
190 | lua_pop(L, 1); | ||
191 | } | ||
192 | if (empty) return NULL; | ||
193 | else return rfds; | ||
194 | } | ||