select implementation.

This commit is contained in:
Diego Nehab 2002-07-03 19:06:53 +00:00
parent b04b81ddd9
commit 9b8bce6465

View File

@ -1,39 +1,43 @@
#include <lua.h>
#include "lspriv.h"
#include "lsselect.h"
#include "lsfd.h"
/* auxiliar functions */
static int local_select(lua_State *L);
static int local_getfd(lua_State *L);
static int local_pending(lua_State *L);
static int local_FD_SET(lua_State *L);
static int local_FD_ISSET(lua_State *L);
static int select_lua_select(lua_State *L);
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Marks type as selectable * Marks type as selectable
* Input * Input
* name: type name * name: type name
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void slct_addclass(lua_State *L, cchar *lsclass) void select_addclass(lua_State *L, cchar *lsclass)
{ {
lua_pushstring(L, "selectable sockets"); lua_pushstring(L, "luasocket(select)");
lua_gettable(L, LUA_REGISTRYINDEX); lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushstring(L, lsclass); lua_pushstring(L, lsclass);
lua_pushnumber(L, 1); lua_pushnumber(L, 1);
lua_settable(L, -3); lua_settable(L, -3);
lua_pop(L, 2); lua_pop(L, 1);
} }
/*-------------------------------------------------------------------------*\ void select_open(lua_State *L)
* 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); /* push select auxiliar lua function and register
lua_pushstring(L, "sock(selectable)"); * select_lua_select with it as an upvalue */
lua_gettable(L, -2); #include "lsselect.loh"
lua_pushstring(L, lua_type(L, -3)); lua_pushcclosure(L, select_lua_select, 1);
lua_gettable(L, -2); lua_setglobal(L, "select");
if (lua_isnil(L, -1)) { /* create luasocket(select) table */
lua_pop(L, 3); lua_pushstring(L, "luasocket(select)");
return NULL; lua_newtable(L);
} else { lua_settable(L, LUA_REGISTRYINDEX);
lua_pop(L, 3);
return (p_sock) lua_touserdata(L, -1);
}
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -47,148 +51,86 @@ static p_sock ls_toselectable(lua_State *L)
* {output}: table with sockets ready for output * {output}: table with sockets ready for output
* err: "timeout" or nil * err: "timeout" or nil
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int global_select(lua_State *L) static int select_lua_select(lua_State *L)
{ {
int ms = lua_isnil(L, 3) ? -1 : (int) (luaL_opt_number(L, 3, -1) * 1000); fd_set read, write;
fd_set rfds, *prfds = NULL, wfds, *pwfds = NULL; FD_ZERO(&read);
struct timeval tv, *ptv = NULL; FD_ZERO(&write);
unsigned max = 0; /* push select lua auxiliar function */
int byfds, readable, writable; lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1);
int toread = 1, towrite = 2; /* make sure we have enough arguments (nil is the default) */
lua_newtable(L); byfds = lua_gettop(L); /* sockets indexed by descriptor */ lua_settop(L, 4);
lua_newtable(L); readable = lua_gettop(L); /* pass FD_SET and manipulation functions */
lua_newtable(L); writable = lua_gettop(L); lua_newuserdatabox(L, &read);
/* collect sockets to be tested into FD_SET structures and fill byfds */ lua_newuserdatabox(L, &write);
if (lua_istable(L, toread)) lua_pushcfunction(L, local_FD_SET);
prfds = tab2rfds(L, toread, &rfds, &max, byfds, readable, &ms); lua_pushcfunction(L, local_FD_ISSET);
else if (!lua_isnil(L, toread)) /* pass getfd function with selectable table as upvalue */
luaL_argerror(L, toread, "expected table or nil"); lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_istable(L, towrite)) lua_pushcclosure(L, local_getfd, 1);
pwfds = tab2wfds(L, towrite, &wfds, &max, byfds); /* pass pending function */
else if (!lua_isnil(L, towrite)) lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
luaL_argerror(L, towrite, "expected table or nil"); lua_pushcclosure(L, local_pending, 1);
/* fill timeval structure */ /* pass select auxiliar C function */
if (ms >= 0) { lua_pushcfunction(L, local_select);
tv.tv_sec = ms / 1000; /* call select auxiliar lua function */
tv.tv_usec = (ms % 1000) * 1000; lua_call(L, 10, 3);
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; return 3;
} }
/*=========================================================================*\ static int local_getfd(lua_State *L)
* 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; priv_pushclass(L, 1);
if (!fds) return; lua_gettable(L, lua_upvalueindex(1));
for (s = 0; s < max; s++) { if (!lua_isnil(L, -1)) {
if (FD_ISSET(s, fds)) { p_fd sock = (p_fd) lua_touserdata(L, 1);
lua_pushnumber(L, lua_getn(L, can) + 1); lua_pushnumber(L, sock->fd);
lua_pushnumber(L, s);
lua_gettable(L, byfds);
lua_settable(L, can);
}
} }
return 1;
} }
/*-------------------------------------------------------------------------*\ static int local_pending(lua_State *L)
* 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; priv_pushclass(L, 1);
FD_ZERO(wfds); lua_gettable(L, lua_upvalueindex(1));
lua_pushnil(L); if (!lua_isnil(L, -1)) {
while (lua_next(L, towrite)) { p_fd sock = (p_fd) lua_touserdata(L, 1);
p_sock sock = ls_toselectable(L); if (sock->fd_pending(L, sock)) lua_pushnumber(L, 1);
if (sock) { /* skip strange fields */ else lua_pushnil(L);
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;
} }
} return 1;
/* get rid of value and expose index */
lua_pop(L, 1);
}
if (empty) return NULL;
else return wfds;
} }
/*-------------------------------------------------------------------------*\ static int local_select(lua_State *L)
* 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; int max_fd = (int) lua_tonumber(L, 1);
FD_ZERO(rfds); fd_set *read_set = (fd_set *) lua_touserdata(L, 2);
lua_pushnil(L); fd_set *write_set = (fd_set *) lua_touserdata(L, 3);
while (lua_next(L, toread)) { int deadline = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000);
p_sock sock = ls_toselectable(L); struct timeval tv;
if (sock) { /* skip strange fields */ if (deadline >= 0) {
NET_FD s = sock->fd; tv.tv_sec = deadline / 1000;
if (s != NET_INVALID) { /* skip closed sockets */ tv.tv_usec = (deadline % 1000) * 1000;
/* a socket can have unread data in our internal buffer. we lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, &tv));
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 { } else {
lua_pushnumber(L, s); lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, NULL));
lua_pushvalue(L, -2);
lua_settable(L, byfds);
if (s > *max) *max = s;
FD_SET(s, rfds);
empty = 0;
} }
} return 1;
} }
/* get rid of value and exposed index */
lua_pop(L, 1); static int local_FD_SET(lua_State *L)
} {
if (empty) return NULL; COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1);
else return rfds; fd_set *set = (fd_set *) lua_touserdata(L, 2);
if (fd >= 0) FD_SET(fd, set);
return 0;
}
static int local_FD_ISSET(lua_State *L)
{
COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1);
fd_set *set = (fd_set *) lua_touserdata(L, 2);
if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1);
else lua_pushnil(L);
return 1;
} }