225 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*=========================================================================*\
 | |
| * Select implementation
 | |
| * LuaSocket toolkit
 | |
| \*=========================================================================*/
 | |
| #include <string.h>
 | |
| 
 | |
| #include "lua.h"
 | |
| #include "lauxlib.h"
 | |
| 
 | |
| #include "socket.h"
 | |
| #include "timeout.h"
 | |
| #include "select.h"
 | |
| 
 | |
| namespace NS_SLUA {    
 | |
| 
 | |
| /*=========================================================================*\
 | |
| * Internal function prototypes.
 | |
| \*=========================================================================*/
 | |
| static t_socket getfd(lua_State *L);
 | |
| static int dirty(lua_State *L);
 | |
| static void collect_fd(lua_State *L, int tab, int itab, 
 | |
|         fd_set *set, t_socket *max_fd);
 | |
| 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 select_func[] = {
 | |
|     {"select", global_select},
 | |
|     {NULL,     NULL}
 | |
| };
 | |
| 
 | |
| /*=========================================================================*\
 | |
| * Exported functions
 | |
| \*=========================================================================*/
 | |
| /*-------------------------------------------------------------------------*\
 | |
| * Initializes module
 | |
| \*-------------------------------------------------------------------------*/
 | |
| int select_open(lua_State *L) {
 | |
|     lua_pushstring(L, "_SETSIZE");
 | |
|     lua_pushnumber(L, FD_SETSIZE);
 | |
|     lua_rawset(L, -3);
 | |
| #if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
 | |
|     luaL_setfuncs(L, select_func, 0);
 | |
| #else
 | |
|     luaL_openlib(L, NULL, select_func, 0);
 | |
| #endif
 | |
|     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 = SOCKET_INVALID;
 | |
|     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);
 | |
|     collect_fd(L, 1, itab, &rset, &max_fd);
 | |
|     collect_fd(L, 2, itab, &wset, &max_fd);
 | |
|     ndirty = check_dirty(L, 1, rtab, &rset);
 | |
|     t = ndirty > 0? 0.0: t;
 | |
|     timeout_init(&tm, t, -1);
 | |
|     timeout_markstart(&tm);
 | |
|     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 {
 | |
|         luaL_error(L, "select failed");
 | |
|         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)) {
 | |
|             double numfd = lua_tonumber(L, -1); 
 | |
|             fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID;
 | |
|         }
 | |
|     } 
 | |
|     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 void collect_fd(lua_State *L, int tab, int itab, 
 | |
|         fd_set *set, t_socket *max_fd) {
 | |
|     int i = 1, n = 0;
 | |
|     /* nil is the same as an empty table */
 | |
|     if (lua_isnil(L, tab)) return;
 | |
|     /* otherwise we need it to be a table */
 | |
|     luaL_checktype(L, tab, LUA_TTABLE);
 | |
|     for ( ;; ) {
 | |
|         t_socket fd;
 | |
|         lua_pushnumber(L, i);
 | |
|         lua_gettable(L, tab);
 | |
|         if (lua_isnil(L, -1)) {
 | |
|             lua_pop(L, 1);
 | |
|             break;
 | |
|         }
 | |
|         /* getfd figures out if this is a socket */
 | |
|         fd = getfd(L);
 | |
|         if (fd != SOCKET_INVALID) {
 | |
|             /* make sure we don't overflow the fd_set */
 | |
| #ifdef _WIN32
 | |
|             if (n >= FD_SETSIZE) 
 | |
|                 luaL_argerror(L, tab, "too many sockets");
 | |
| #else
 | |
|             if (fd >= FD_SETSIZE) 
 | |
|                 luaL_argerror(L, tab, "descriptor too large for set size");
 | |
| #endif
 | |
|             FD_SET(fd, set);
 | |
|             n++;
 | |
|             /* keep track of the largest descriptor so far */
 | |
|             if (*max_fd == SOCKET_INVALID || *max_fd < fd) 
 | |
|                 *max_fd = fd;
 | |
|             /* make sure we can map back from descriptor to the object */
 | |
|             lua_pushnumber(L, (lua_Number) fd);
 | |
|             lua_pushvalue(L, -2);
 | |
|             lua_settable(L, itab);
 | |
|         }
 | |
|         lua_pop(L, 1);
 | |
|         i = i + 1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     for ( ;; ) { 
 | |
|         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, (lua_Number) 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);
 | |
|     for ( ;; ) {
 | |
|         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;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| } // end NS_SLUA
 |