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 | } | ||
