From 3bd99ec59996653011f0eb40a0be4a523fe82897 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Fri, 22 Mar 2002 20:07:43 +0000 Subject: [PATCH] Initial revision --- src/select.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/select.c 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 @@ +/*-------------------------------------------------------------------------*\ +* 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; +}