# HG changeset patch # User jbe # Date 1428104305 -7200 # Node ID 5df424e743835542568c58dc06a20cfcab374cd5 # Parent 12950c28b73a78802a58d3c518b1925e848f6bf6 Work on support for non-blocking I/O diff -r 12950c28b73a -r 5df424e74383 moonbridge.c --- a/moonbridge.c Mon Mar 30 00:44:18 2015 +0200 +++ b/moonbridge.c Sat Apr 04 01:38:25 2015 +0200 @@ -2202,6 +2202,27 @@ return 1; } +static int moonbr_lua_tonatural(lua_State *L, int idx) { + int isnum; + lua_Number n; + n = lua_tonumberx(L, idx, &isnum); + if (isnum && n>=0 && n=0 && n<=100000000) { + value->tv_sec = n; + value->tv_usec = 1e6 * (n - value->tv_sec); + return 1; + } else { + return 0; + } +} + /* Memory allocator that allows limiting memory consumption */ static void *moonbr_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; /* not used */ @@ -2236,8 +2257,164 @@ return ptr; } +/* New function io.poll(...) */ +static int moonbr_io_poll(lua_State *L) { + int idx_tbl = 1; + int idx_timeout = 0; + int i; + int fd, isnum; + int nfds = 0; + fd_set readfds, writefds, exceptfds; + struct timeval timeout = {0, }; + int status; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + if (lua_type(L, 1) == LUA_TNUMBER) { + idx_timeout = 1; + idx_tbl = 2; + } + luaL_checktype(L, idx_tbl, LUA_TTABLE); + for (i=1; ; i++) { +#if LUA_VERSION_NUM >= 503 + if (lua_geti(L, idx_tbl, i) == LUA_TNIL) break; +#else + lua_pushinteger(L, i); + lua_gettable(L, idx_tbl); + if (lua_isnil(L, -1)) break; +#endif + fd = lua_tointegerx(L, -1, &isnum); + if (!isnum) luaL_error(L, "File descriptor is not an integer"); + FD_SET(fd, &readfds); + if (fd+1 > nfds) nfds = fd+1; + lua_pop(L, 1); + } + if (!idx_timeout && lua_type(L, 2) == LUA_TNUMBER) { + idx_timeout = 2; + idx_tbl = 3; + } else { + idx_tbl = 2; + } + luaL_checktype(L, idx_tbl, LUA_TTABLE); + for (i=1; ; i++) { +#if LUA_VERSION_NUM >= 503 + if (lua_geti(L, idx_tbl, i) == LUA_TNIL) break; +#else + lua_pushinteger(L, i); + lua_gettable(L, idx_tbl); + if (lua_isnil(L, -1)) break; +#endif + fd = lua_tointegerx(L, -1, &isnum); + if (!isnum) luaL_error(L, "File descriptor is not an integer"); + FD_SET(fd, &writefds); + if (fd+1 > nfds) nfds = fd+1; + lua_pop(L, 1); + } + lua_pop(L, 2); + if (!idx_timeout && !lua_isnoneornil(L, 3)) idx_timeout = 3; + if (idx_timeout) { + luaL_argcheck( + L, + moonbr_lua_totimeval(L, idx_timeout, &timeout), + idx_timeout, + "not a valid timeout" + ); + } + status = select(nfds, &readfds, &writefds, &exceptfds, &timeout); + if (status == -1) { + if (errno == EINTR) { + lua_pushboolean(L, 0); + } else { + char errmsg[MOONBR_MAXSTRERRORLEN]; + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ + luaL_error(L, "Unexpected error during \"select\" system call: %s", errmsg); + } + } else if (status == 0) { + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +/* New method for Lua file objects: get file descriptor */ +static int moonbr_io_getfd(lua_State *L) { + luaL_Stream *stream; + stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); + if (!stream->closef) luaL_error(L, "attempt to use a closed file"); + lua_pushinteger(L, fileno(stream->f)); + return 1; +} + +/* New method for Lua file objects: check if non-blocking reading is possible */ +static int moonbr_io_pending(lua_State *L) { + luaL_Stream *stream; + FILE *file; + int fd, flags, chr; + stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); + if (!stream->closef) luaL_error(L, "attempt to use a closed file"); + file = stream->f; + flockfile(file); + fd = fileno_unlocked(file); + if (ferror(file) || feof(file)) { + funlockfile(file); + lua_pushboolean(L, 1); + return 1; + } + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) goto moonbr_io_pending_error; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) goto moonbr_io_pending_error; + chr = getc_unlocked(file); + lua_pushboolean(L, 1); + if (chr == EOF) { + if (ferror(file) && errno == EAGAIN) { + clearerr_unlocked(file); + lua_pushboolean(L, 0); + } + } else { + if (ungetc(chr, file) == EOF) goto moonbr_io_pending_error; + } + if (fcntl(fd, F_SETFL, flags) == -1) goto moonbr_io_pending_error; + funlockfile(file); + return 1; + moonbr_io_pending_error: + funlockfile(file); + { + char errmsg[MOONBR_MAXSTRERRORLEN]; + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ + luaL_error(L, "Unexpected error in method \"pending\" of FILE handle: %s", errmsg); + } + return 0; +} + +/* New method for Lua file objects: set blocking or non-blocking I/O */ +static int moonbr_io_setblocking(lua_State *L) { + luaL_Stream *stream; + int blocking; + int fd, flags; + stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); + luaL_checktype(L, 2, LUA_TBOOLEAN); + blocking = lua_toboolean(L, 2); + if (!stream->closef) luaL_error(L, "attempt to use a closed file"); + fd = fileno_unlocked(stream->f); + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) goto moonbr_io_setblocking_error; + if (blocking) flags &= ~ O_NONBLOCK; + else flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) goto moonbr_io_setblocking_error; + lua_pushboolean(L, 1); + return 1; + moonbr_io_setblocking_error: + { + char errmsg[MOONBR_MAXSTRERRORLEN]; + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ + luaL_error(L, "Unexpected error in method \"setblocking\" of FILE handle: %s", errmsg); + } + return 0; +} + /* New method for Lua file objects: read until terminator or length exceeded */ -static int moonbr_readuntil(lua_State *L) { +static int moonbr_io_readuntil(lua_State *L) { luaL_Stream *stream; FILE *file; const char *terminatorstr; @@ -2276,27 +2453,6 @@ return 1; } -static int moonbr_lua_tonatural(lua_State *L, int idx) { - int isnum; - lua_Number n; - n = lua_tonumberx(L, idx, &isnum); - if (isnum && n>=0 && n=0 && n<=100000000) { - value->tv_sec = n; - value->tv_usec = 1e6 * (n - value->tv_sec); - return 1; - } else { - return 0; - } -} - static int moonbr_timeout(lua_State *L) { struct itimerval oldval; if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2)) { @@ -2764,12 +2920,22 @@ #ifdef MOONBR_LUA_CPATH moonbr_modify_path(L, "cpath", MOONBR_LUA_CPATH); #endif + lua_getglobal(L, "io"); + lua_pushcfunction(L, moonbr_io_poll); + lua_setfield(L, -2, "poll"); + lua_pop(L, 1); if (luaL_newmetatable(L, LUA_FILEHANDLE)) { moonbr_log(LOG_CRIT, "Lua metatable LUA_FILEHANDLE does not exist"); moonbr_terminate_error(); } lua_getfield(L, -1, "__index"); - lua_pushcfunction(L, moonbr_readuntil); + lua_pushcfunction(L, moonbr_io_getfd); + lua_setfield(L, -2, "getfd"); + lua_pushcfunction(L, moonbr_io_pending); + lua_setfield(L, -2, "pending"); + lua_pushcfunction(L, moonbr_io_setblocking); + lua_setfield(L, -2, "setblocking"); + lua_pushcfunction(L, moonbr_io_readuntil); lua_setfield(L, -2, "readuntil"); lua_pop(L, 2); lua_pushcfunction(L, moonbr_timeout);