| /*=========================================================================*\ |
| * Select implementation |
| * LuaSocket toolkit |
| * |
| * RCS ID: $Id: select.c,v 1.22 2005/11/20 07:20:23 diego Exp $ |
| \*=========================================================================*/ |
| #include <string.h> |
| |
| #include "lua.h" |
| #include "lauxlib.h" |
| |
| #include "socket.h" |
| #include "timeout.h" |
| #include "select.h" |
| |
| /*=========================================================================*\ |
| * Internal function prototypes. |
| \*=========================================================================*/ |
| static t_socket getfd(lua_State *L); |
| static int dirty(lua_State *L); |
| static t_socket collect_fd(lua_State *L, int tab, t_socket max_fd, |
| int itab, fd_set *set); |
| static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); |
| static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, |
| int itab, int tab, int start); |
| static void make_assoc(lua_State *L, int tab); |
| static int global_select(lua_State *L); |
| |
| /* functions in library namespace */ |
| static luaL_reg func[] = { |
| {"select", global_select}, |
| {NULL, NULL} |
| }; |
| |
| /*=========================================================================*\ |
| * Exported functions |
| \*=========================================================================*/ |
| /*-------------------------------------------------------------------------*\ |
| * Initializes module |
| \*-------------------------------------------------------------------------*/ |
| int select_open(lua_State *L) { |
| luaL_openlib(L, NULL, func, 0); |
| return 0; |
| } |
| |
| /*=========================================================================*\ |
| * Global Lua functions |
| \*=========================================================================*/ |
| /*-------------------------------------------------------------------------*\ |
| * Waits for a set of sockets until a condition is met or timeout. |
| \*-------------------------------------------------------------------------*/ |
| static int global_select(lua_State *L) { |
| int rtab, wtab, itab, ret, ndirty; |
| t_socket max_fd; |
| fd_set rset, wset; |
| t_timeout tm; |
| double t = luaL_optnumber(L, 3, -1); |
| FD_ZERO(&rset); FD_ZERO(&wset); |
| lua_settop(L, 3); |
| lua_newtable(L); itab = lua_gettop(L); |
| lua_newtable(L); rtab = lua_gettop(L); |
| lua_newtable(L); wtab = lua_gettop(L); |
| max_fd = collect_fd(L, 1, SOCKET_INVALID, itab, &rset); |
| ndirty = check_dirty(L, 1, rtab, &rset); |
| t = ndirty > 0? 0.0: t; |
| timeout_init(&tm, t, -1); |
| timeout_markstart(&tm); |
| max_fd = collect_fd(L, 2, max_fd, itab, &wset); |
| ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm); |
| if (ret > 0 || ndirty > 0) { |
| return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); |
| return_fd(L, &wset, max_fd+1, itab, wtab, 0); |
| make_assoc(L, rtab); |
| make_assoc(L, wtab); |
| return 2; |
| } else if (ret == 0) { |
| lua_pushstring(L, "timeout"); |
| return 3; |
| } else { |
| lua_pushstring(L, "error"); |
| return 3; |
| } |
| } |
| |
| /*=========================================================================*\ |
| * Internal functions |
| \*=========================================================================*/ |
| static t_socket getfd(lua_State *L) { |
| t_socket fd = SOCKET_INVALID; |
| lua_pushstring(L, "getfd"); |
| lua_gettable(L, -2); |
| if (!lua_isnil(L, -1)) { |
| lua_pushvalue(L, -2); |
| lua_call(L, 1, 1); |
| if (lua_isnumber(L, -1)) |
| fd = (t_socket) lua_tonumber(L, -1); |
| } |
| lua_pop(L, 1); |
| return fd; |
| } |
| |
| static int dirty(lua_State *L) { |
| int is = 0; |
| lua_pushstring(L, "dirty"); |
| lua_gettable(L, -2); |
| if (!lua_isnil(L, -1)) { |
| lua_pushvalue(L, -2); |
| lua_call(L, 1, 1); |
| is = lua_toboolean(L, -1); |
| } |
| lua_pop(L, 1); |
| return is; |
| } |
| |
| static t_socket collect_fd(lua_State *L, int tab, t_socket max_fd, |
| int itab, fd_set *set) { |
| int i = 1; |
| if (lua_isnil(L, tab)) |
| return max_fd; |
| while (1) { |
| t_socket fd; |
| lua_pushnumber(L, i); |
| lua_gettable(L, tab); |
| if (lua_isnil(L, -1)) { |
| lua_pop(L, 1); |
| break; |
| } |
| fd = getfd(L); |
| if (fd != SOCKET_INVALID) { |
| FD_SET(fd, set); |
| if (max_fd == SOCKET_INVALID || max_fd < fd) |
| max_fd = fd; |
| lua_pushnumber(L, fd); |
| lua_pushvalue(L, -2); |
| lua_settable(L, itab); |
| } |
| lua_pop(L, 1); |
| i = i + 1; |
| } |
| return max_fd; |
| } |
| |
| static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { |
| int ndirty = 0, i = 1; |
| if (lua_isnil(L, tab)) |
| return 0; |
| while (1) { |
| t_socket fd; |
| lua_pushnumber(L, i); |
| lua_gettable(L, tab); |
| if (lua_isnil(L, -1)) { |
| lua_pop(L, 1); |
| break; |
| } |
| fd = getfd(L); |
| if (fd != SOCKET_INVALID && dirty(L)) { |
| lua_pushnumber(L, ++ndirty); |
| lua_pushvalue(L, -2); |
| lua_settable(L, dtab); |
| FD_CLR(fd, set); |
| } |
| lua_pop(L, 1); |
| i = i + 1; |
| } |
| return ndirty; |
| } |
| |
| static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, |
| int itab, int tab, int start) { |
| t_socket fd; |
| for (fd = 0; fd < max_fd; fd++) { |
| if (FD_ISSET(fd, set)) { |
| lua_pushnumber(L, ++start); |
| lua_pushnumber(L, fd); |
| lua_gettable(L, itab); |
| lua_settable(L, tab); |
| } |
| } |
| } |
| |
| static void make_assoc(lua_State *L, int tab) { |
| int i = 1, atab; |
| lua_newtable(L); atab = lua_gettop(L); |
| while (1) { |
| lua_pushnumber(L, i); |
| lua_gettable(L, tab); |
| if (!lua_isnil(L, -1)) { |
| lua_pushnumber(L, i); |
| lua_pushvalue(L, -2); |
| lua_settable(L, atab); |
| lua_pushnumber(L, i); |
| lua_settable(L, atab); |
| } else { |
| lua_pop(L, 1); |
| break; |
| } |
| i = i+1; |
| } |
| } |
| |