diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2002-07-03 19:06:53 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2002-07-03 19:06:53 +0000 |
commit | 9b8bce6465d2c7de84782f9e12f529a020a16444 (patch) | |
tree | 8a9e7319c1e60e015364dbf22d03aba7d38cd550 | |
parent | b04b81ddd959948143f817ee7230783660e24222 (diff) | |
download | luasocket-9b8bce6465d2c7de84782f9e12f529a020a16444.tar.gz luasocket-9b8bce6465d2c7de84782f9e12f529a020a16444.tar.bz2 luasocket-9b8bce6465d2c7de84782f9e12f529a020a16444.zip |
select implementation.
-rw-r--r-- | src/select.c | 248 |
1 files changed, 95 insertions, 153 deletions
diff --git a/src/select.c b/src/select.c index 1f83b7a..9a24dbb 100644 --- a/src/select.c +++ b/src/select.c | |||
@@ -1,39 +1,43 @@ | |||
1 | #include <lua.h> | ||
2 | #include "lspriv.h" | ||
3 | #include "lsselect.h" | ||
4 | #include "lsfd.h" | ||
5 | |||
6 | /* auxiliar functions */ | ||
7 | static int local_select(lua_State *L); | ||
8 | static int local_getfd(lua_State *L); | ||
9 | static int local_pending(lua_State *L); | ||
10 | static int local_FD_SET(lua_State *L); | ||
11 | static int local_FD_ISSET(lua_State *L); | ||
12 | |||
13 | static int select_lua_select(lua_State *L); | ||
14 | |||
1 | /*-------------------------------------------------------------------------*\ | 15 | /*-------------------------------------------------------------------------*\ |
2 | * Marks type as selectable | 16 | * Marks type as selectable |
3 | * Input | 17 | * Input |
4 | * name: type name | 18 | * name: type name |
5 | \*-------------------------------------------------------------------------*/ | 19 | \*-------------------------------------------------------------------------*/ |
6 | void slct_addclass(lua_State *L, cchar *lsclass) | 20 | void select_addclass(lua_State *L, cchar *lsclass) |
7 | { | 21 | { |
8 | lua_pushstring(L, "selectable sockets"); | 22 | lua_pushstring(L, "luasocket(select)"); |
9 | lua_gettable(L, LUA_REGISTRYINDEX); | 23 | lua_gettable(L, LUA_REGISTRYINDEX); |
10 | lua_pushstring(L, lsclass); | 24 | lua_pushstring(L, lsclass); |
11 | lua_pushnumber(L, 1); | 25 | lua_pushnumber(L, 1); |
12 | lua_settable(L, -3); | 26 | lua_settable(L, -3); |
13 | lua_pop(L, 2); | 27 | lua_pop(L, 1); |
14 | } | 28 | } |
15 | 29 | ||
16 | /*-------------------------------------------------------------------------*\ | 30 | void select_open(lua_State *L) |
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 | { | 31 | { |
25 | lua_getregistry(L); | 32 | /* push select auxiliar lua function and register |
26 | lua_pushstring(L, "sock(selectable)"); | 33 | * select_lua_select with it as an upvalue */ |
27 | lua_gettable(L, -2); | 34 | #include "lsselect.loh" |
28 | lua_pushstring(L, lua_type(L, -3)); | 35 | lua_pushcclosure(L, select_lua_select, 1); |
29 | lua_gettable(L, -2); | 36 | lua_setglobal(L, "select"); |
30 | if (lua_isnil(L, -1)) { | 37 | /* create luasocket(select) table */ |
31 | lua_pop(L, 3); | 38 | lua_pushstring(L, "luasocket(select)"); |
32 | return NULL; | 39 | lua_newtable(L); |
33 | } else { | 40 | lua_settable(L, LUA_REGISTRYINDEX); |
34 | lua_pop(L, 3); | ||
35 | return (p_sock) lua_touserdata(L, -1); | ||
36 | } | ||
37 | } | 41 | } |
38 | 42 | ||
39 | /*-------------------------------------------------------------------------*\ | 43 | /*-------------------------------------------------------------------------*\ |
@@ -47,148 +51,86 @@ static p_sock ls_toselectable(lua_State *L) | |||
47 | * {output}: table with sockets ready for output | 51 | * {output}: table with sockets ready for output |
48 | * err: "timeout" or nil | 52 | * err: "timeout" or nil |
49 | \*-------------------------------------------------------------------------*/ | 53 | \*-------------------------------------------------------------------------*/ |
50 | int global_select(lua_State *L) | 54 | static int select_lua_select(lua_State *L) |
51 | { | 55 | { |
52 | int ms = lua_isnil(L, 3) ? -1 : (int) (luaL_opt_number(L, 3, -1) * 1000); | 56 | fd_set read, write; |
53 | fd_set rfds, *prfds = NULL, wfds, *pwfds = NULL; | 57 | FD_ZERO(&read); |
54 | struct timeval tv, *ptv = NULL; | 58 | FD_ZERO(&write); |
55 | unsigned max = 0; | 59 | /* push select lua auxiliar function */ |
56 | int byfds, readable, writable; | 60 | lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1); |
57 | int toread = 1, towrite = 2; | 61 | /* make sure we have enough arguments (nil is the default) */ |
58 | lua_newtable(L); byfds = lua_gettop(L); /* sockets indexed by descriptor */ | 62 | lua_settop(L, 4); |
59 | lua_newtable(L); readable = lua_gettop(L); | 63 | /* pass FD_SET and manipulation functions */ |
60 | lua_newtable(L); writable = lua_gettop(L); | 64 | lua_newuserdatabox(L, &read); |
61 | /* collect sockets to be tested into FD_SET structures and fill byfds */ | 65 | lua_newuserdatabox(L, &write); |
62 | if (lua_istable(L, toread)) | 66 | lua_pushcfunction(L, local_FD_SET); |
63 | prfds = tab2rfds(L, toread, &rfds, &max, byfds, readable, &ms); | 67 | lua_pushcfunction(L, local_FD_ISSET); |
64 | else if (!lua_isnil(L, toread)) | 68 | /* pass getfd function with selectable table as upvalue */ |
65 | luaL_argerror(L, toread, "expected table or nil"); | 69 | lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX); |
66 | if (lua_istable(L, towrite)) | 70 | lua_pushcclosure(L, local_getfd, 1); |
67 | pwfds = tab2wfds(L, towrite, &wfds, &max, byfds); | 71 | /* pass pending function */ |
68 | else if (!lua_isnil(L, towrite)) | 72 | lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX); |
69 | luaL_argerror(L, towrite, "expected table or nil"); | 73 | lua_pushcclosure(L, local_pending, 1); |
70 | /* fill timeval structure */ | 74 | /* pass select auxiliar C function */ |
71 | if (ms >= 0) { | 75 | lua_pushcfunction(L, local_select); |
72 | tv.tv_sec = ms / 1000; | 76 | /* call select auxiliar lua function */ |
73 | tv.tv_usec = (ms % 1000) * 1000; | 77 | lua_call(L, 10, 3); |
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; | 78 | return 3; |
86 | } | 79 | } |
87 | 80 | ||
88 | /*=========================================================================*\ | 81 | static int local_getfd(lua_State *L) |
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 | { | 82 | { |
101 | int s; | 83 | priv_pushclass(L, 1); |
102 | if (!fds) return; | 84 | lua_gettable(L, lua_upvalueindex(1)); |
103 | for (s = 0; s < max; s++) { | 85 | if (!lua_isnil(L, -1)) { |
104 | if (FD_ISSET(s, fds)) { | 86 | p_fd sock = (p_fd) lua_touserdata(L, 1); |
105 | lua_pushnumber(L, lua_getn(L, can) + 1); | 87 | lua_pushnumber(L, sock->fd); |
106 | lua_pushnumber(L, s); | ||
107 | lua_gettable(L, byfds); | ||
108 | lua_settable(L, can); | ||
109 | } | ||
110 | } | 88 | } |
89 | return 1; | ||
111 | } | 90 | } |
112 | 91 | ||
113 | /*-------------------------------------------------------------------------*\ | 92 | static int local_pending(lua_State *L) |
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 | { | 93 | { |
125 | int empty = 1; | 94 | priv_pushclass(L, 1); |
126 | FD_ZERO(wfds); | 95 | lua_gettable(L, lua_upvalueindex(1)); |
127 | lua_pushnil(L); | 96 | if (!lua_isnil(L, -1)) { |
128 | while (lua_next(L, towrite)) { | 97 | p_fd sock = (p_fd) lua_touserdata(L, 1); |
129 | p_sock sock = ls_toselectable(L); | 98 | if (sock->fd_pending(L, sock)) lua_pushnumber(L, 1); |
130 | if (sock) { /* skip strange fields */ | 99 | else lua_pushnil(L); |
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 | } | 100 | } |
144 | if (empty) return NULL; | 101 | return 1; |
145 | else return wfds; | ||
146 | } | 102 | } |
147 | 103 | ||
148 | /*-------------------------------------------------------------------------*\ | 104 | static int local_select(lua_State *L) |
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 | { | 105 | { |
162 | int empty = 1; | 106 | int max_fd = (int) lua_tonumber(L, 1); |
163 | FD_ZERO(rfds); | 107 | fd_set *read_set = (fd_set *) lua_touserdata(L, 2); |
164 | lua_pushnil(L); | 108 | fd_set *write_set = (fd_set *) lua_touserdata(L, 3); |
165 | while (lua_next(L, toread)) { | 109 | int deadline = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); |
166 | p_sock sock = ls_toselectable(L); | 110 | struct timeval tv; |
167 | if (sock) { /* skip strange fields */ | 111 | if (deadline >= 0) { |
168 | NET_FD s = sock->fd; | 112 | tv.tv_sec = deadline / 1000; |
169 | if (s != NET_INVALID) { /* skip closed sockets */ | 113 | tv.tv_usec = (deadline % 1000) * 1000; |
170 | /* a socket can have unread data in our internal buffer. we | 114 | lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, &tv)); |
171 | pass them straight to the readable set, and test only to | 115 | } else { |
172 | find out which of the other sockets can be written to or | 116 | lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, NULL)); |
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 | } | 117 | } |
192 | if (empty) return NULL; | 118 | return 1; |
193 | else return rfds; | 119 | } |
120 | |||
121 | static int local_FD_SET(lua_State *L) | ||
122 | { | ||
123 | COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1); | ||
124 | fd_set *set = (fd_set *) lua_touserdata(L, 2); | ||
125 | if (fd >= 0) FD_SET(fd, set); | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int local_FD_ISSET(lua_State *L) | ||
130 | { | ||
131 | COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1); | ||
132 | fd_set *set = (fd_set *) lua_touserdata(L, 2); | ||
133 | if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1); | ||
134 | else lua_pushnil(L); | ||
135 | return 1; | ||
194 | } | 136 | } |