mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-27 04:48:21 +01:00
select implementation.
This commit is contained in:
parent
b04b81ddd9
commit
9b8bce6465
242
src/select.c
242
src/select.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
/* get rid of value and exposed index */
|
|
||||||
lua_pop(L, 1);
|
static int local_FD_ISSET(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_ISSET(fd, set)) lua_pushnumber(L, 1);
|
||||||
|
else lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user