# HG changeset patch # User jbe # Date 1428284197 -7200 # Node ID e1aec772a6ab82659e5d21eee6338de625b53497 # Parent 1a0346580e6d1d4c153ad6d10dc4df9191622dc7 Implementation of write, write_nb, flush, flush_nb with double buffering technique diff -r 1a0346580e6d -r e1aec772a6ab moonbridge_io.c --- a/moonbridge_io.c Mon Apr 06 01:35:23 2015 +0200 +++ b/moonbridge_io.c Mon Apr 06 03:36:37 2015 +0200 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -27,10 +28,31 @@ typedef struct { int fd; - int writecnt; + int nonblocking; + int writeerr; + int writeleft; + int writeqoff; + int writebufcnt; char writebuf[MOONBR_IO_WRITEBUFLEN]; } moonbr_io_handle_t; +static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) { + if (handle->nonblocking != nonblocking) { + int flags; + flags = fcntl(handle->fd, F_GETFL, 0); + if (flags == -1) { + moonbr_io_errmsg(); + luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); + } + if (nonblocking) flags |= O_NONBLOCK; + else flags &= ~O_NONBLOCK; + if (fcntl(handle->fd, F_SETFL, flags) == -1) { + moonbr_io_errmsg(); + luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); + } + } +} + static int moonbr_io_close(lua_State *L) { moonbr_io_handle_t *handle; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); @@ -47,92 +69,148 @@ return 1; } -static int moonbr_io_write(lua_State *L) { +static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) { moonbr_io_handle_t *handle; int i, top; + lua_Integer qlen, qpos, qpos2; const char *str; - size_t strlen; + size_t strlen, strpos; size_t written; ssize_t result; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - if (handle->writecnt < 0) { + if (handle->writeerr) { lua_pushnil(L); lua_pushliteral(L, "Previous write error"); return 2; } - top = lua_gettop(L); - for (i=2; i<=top; i++) { - str = luaL_checklstring(L, i, &strlen); - while (strlen) { - if (strlen < MOONBR_IO_WRITEBUFLEN - handle->writecnt) { - memcpy(handle->writebuf + handle->writecnt, str, strlen); - handle->writecnt += strlen; + moonbr_io_handle_set_nonblocking(L, handle, nonblocking); + if (!flush) top = lua_gettop(L); + lua_getuservalue(L, 1); + lua_getfield(L, -1, "writebuf"); + qlen = lua_rawlen(L, -1); + if (!flush) { + for (i=2; i<=top; i++) { + luaL_checklstring(L, i, &strlen); + lua_pushvalue(L, i); + lua_rawseti(L, -2, ++qlen); + handle->writeleft += strlen; + } + } + for (qpos=1; qpos<=qlen; qpos++) { + lua_rawgeti(L, -1, qpos); + str = lua_tolstring(L, -1, &strlen); + strpos = handle->writeqoff; + while (strpos < strlen) { + if (strlen - strpos < MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) { + memcpy(handle->writebuf + handle->writebufcnt, str + strpos, strlen - strpos); + handle->writebufcnt += strlen - strpos; break; } else { written = 0; - memcpy(handle->writebuf + handle->writecnt, str, MOONBR_IO_WRITEBUFLEN - handle->writecnt); + memcpy(handle->writebuf + handle->writebufcnt, str + strpos, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt); + strpos += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt; while (written < MOONBR_IO_WRITEBUFLEN) { result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written); if (result < 0) { - if (errno != EINTR) { + if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { + if (written) { + handle->writebufcnt -= written; + memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); + } + if (i > 1) { + for (qpos2=1; qpos2<=qlen; qpos2++) { + if (qpos2+qpos-1 <= qlen) lua_rawgeti(L, -1, qpos2+qpos-1); + else lua_pushnil(L); + lua_rawseti(L, -2, qpos2); + } + } + handle->writeqoff = strpos; + goto moonbr_io_write_impl_block; + } else if (errno != EINTR) { moonbr_io_errmsg(); - handle->writecnt = -1; + handle->writeerr = 1; lua_pushnil(L); lua_pushstring(L, errmsg); return 2; } } else { written += result; + handle->writeleft -= result; } } - str += MOONBR_IO_WRITEBUFLEN - handle->writecnt; - strlen -= MOONBR_IO_WRITEBUFLEN - handle->writecnt; - handle->writecnt = 0; + handle->writebufcnt = 0; } } + handle->writeqoff = 0; + lua_pop(L, 1); + lua_pushnil(L); + lua_rawseti(L, -2, qpos); } - lua_settop(L, 1); + if (flush) { + written = 0; + while (written < handle->writebufcnt) { + result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written); + if (result < 0) { + if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { + if (written) { + handle->writebufcnt -= written; + memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); + } + goto moonbr_io_write_impl_block; + } else if (errno != EINTR) { + moonbr_io_errmsg(); + handle->writeerr = -1; + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + } else { + written += result; + handle->writeleft -= result; + } + } + handle->writebufcnt = 0; + lua_pushinteger(L, 0); + } else { + lua_pushinteger(L, handle->writeleft - handle->writebufcnt); + } return 1; + moonbr_io_write_impl_block: + if (flush) lua_pushinteger(L, handle->writeleft); + else lua_pushinteger(L, handle->writeleft - handle->writebufcnt); + return 1; +} + +static int moonbr_io_write(lua_State *L) { + return moonbr_io_write_impl(L, 0, 0); +} + +static int moonbr_io_write_nb(lua_State *L) { + return moonbr_io_write_impl(L, 1, 0); } static int moonbr_io_flush(lua_State *L) { - moonbr_io_handle_t *handle; - size_t written; - ssize_t result; - handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); - if (handle->writecnt < 0) { - lua_pushnil(L); - lua_pushliteral(L, "Previous write error"); - return 2; - } - written = 0; - while (written < handle->writecnt) { - result = write(handle->fd, handle->writebuf + written, handle->writecnt - written); - if (result < 0) { - if (errno != EINTR) { - moonbr_io_errmsg(); - handle->writecnt = -1; - lua_pushnil(L); - lua_pushstring(L, errmsg); - return 2; - } - } else { - written += result; - } - } - handle->writecnt = 0; - lua_settop(L, 1); - return 1; + return moonbr_io_write_impl(L, 0, 1); +} + +static int moonbr_io_flush_nb(lua_State *L) { + return moonbr_io_write_impl(L, 1, 1); } void moonbr_io_pushhandle(lua_State *L, int fd, int gc_idx) { moonbr_io_handle_t *handle; handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); handle->fd = fd; - handle->writecnt = 0; + handle->nonblocking = -1; + handle->writeerr = 0; + handle->writeleft = 0; + handle->writeqoff = 0; + handle->writebufcnt = 0; luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY); lua_setmetatable(L, -2); lua_newtable(L); // uservalue + lua_newtable(L); + lua_setfield(L, -2, "writebuf"); if (gc_idx) lua_pushvalue(L, gc_idx); else lua_pushcfunction(L, moonbr_io_close); lua_setfield(L, -2, "gc"); @@ -182,7 +260,9 @@ static const struct luaL_Reg moonbr_io_handle_methods[] = { {"close", moonbr_io_close}, {"write", moonbr_io_write}, + {"write_nb", moonbr_io_write_nb}, {"flush", moonbr_io_flush}, + {"flush_nb", moonbr_io_flush_nb}, {NULL, NULL} };