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
* Input
* 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_pushstring(L, lsclass);
lua_pushnumber(L, 1);
lua_settable(L, -3);
lua_pop(L, 2);
lua_pop(L, 1);
}
/*-------------------------------------------------------------------------*\
* 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)
void select_open(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);
}
/* push select auxiliar lua function and register
* select_lua_select with it as an upvalue */
#include "lsselect.loh"
lua_pushcclosure(L, select_lua_select, 1);
lua_setglobal(L, "select");
/* create luasocket(select) table */
lua_pushstring(L, "luasocket(select)");
lua_newtable(L);
lua_settable(L, LUA_REGISTRYINDEX);
}
/*-------------------------------------------------------------------------*\
@ -47,148 +51,86 @@ static p_sock ls_toselectable(lua_State *L)
* {output}: table with sockets ready for output
* 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 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);
fd_set read, write;
FD_ZERO(&read);
FD_ZERO(&write);
/* push select lua auxiliar function */
lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1);
/* make sure we have enough arguments (nil is the default) */
lua_settop(L, 4);
/* pass FD_SET and manipulation functions */
lua_newuserdatabox(L, &read);
lua_newuserdatabox(L, &write);
lua_pushcfunction(L, local_FD_SET);
lua_pushcfunction(L, local_FD_ISSET);
/* pass getfd function with selectable table as upvalue */
lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushcclosure(L, local_getfd, 1);
/* pass pending function */
lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushcclosure(L, local_pending, 1);
/* pass select auxiliar C function */
lua_pushcfunction(L, local_select);
/* call select auxiliar lua function */
lua_call(L, 10, 3);
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)
static int local_getfd(lua_State *L)
{
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);
}
priv_pushclass(L, 1);
lua_gettable(L, lua_upvalueindex(1));
if (!lua_isnil(L, -1)) {
p_fd sock = (p_fd) lua_touserdata(L, 1);
lua_pushnumber(L, sock->fd);
}
return 1;
}
/*-------------------------------------------------------------------------*\
* 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)
static int local_pending(lua_State *L)
{
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);
priv_pushclass(L, 1);
lua_gettable(L, lua_upvalueindex(1));
if (!lua_isnil(L, -1)) {
p_fd sock = (p_fd) lua_touserdata(L, 1);
if (sock->fd_pending(L, sock)) lua_pushnumber(L, 1);
else lua_pushnil(L);
}
if (empty) return NULL;
else return wfds;
return 1;
}
/*-------------------------------------------------------------------------*\
* 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)
static int local_select(lua_State *L)
{
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);
int max_fd = (int) lua_tonumber(L, 1);
fd_set *read_set = (fd_set *) lua_touserdata(L, 2);
fd_set *write_set = (fd_set *) lua_touserdata(L, 3);
int deadline = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000);
struct timeval tv;
if (deadline >= 0) {
tv.tv_sec = deadline / 1000;
tv.tv_usec = (deadline % 1000) * 1000;
lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, &tv));
} else {
lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, NULL));
}
if (empty) return NULL;
else return rfds;
return 1;
}
static int local_FD_SET(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_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;
}