Software /
code /
prosody
File
util-src/poll.c @ 11640:51598e46e136
util.stanza: Simplify and make pretty-printing look nicer
I've had this color theme in a local debug module for some time and I
quite like it. The colors are from the XMPP logo.
Removes extra XML serialization implementation in favor of the standard
one. Also removes recursive str=str..more string building.
The new two-level gsub has the accumulator in C space so shouldn't be
too bad. The inner gsub calls use no callback, so should be fast and
not create all that much garbage.
No serious benchmarking has been done, but who cares if it looks nice?
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 07 Nov 2020 22:09:46 +0100 |
parent | 10921:6eb5d2bb11af |
child | 12314:898554323338 |
line wrap: on
line source
/* * Lua polling library * Copyright (C) 2017-2018 Kim Alvefur * * This project is MIT licensed. Please see the * COPYING file in the source package for more information. * */ #include <unistd.h> #include <string.h> #include <errno.h> #ifdef __linux__ #define USE_EPOLL #endif #ifdef USE_EPOLL #include <sys/epoll.h> #ifndef MAX_EVENTS #define MAX_EVENTS 64 #endif #else #include <sys/select.h> #endif #include <lualib.h> #include <lauxlib.h> #ifdef USE_EPOLL #define STATE_MT "util.poll<epoll>" #else #define STATE_MT "util.poll<select>" #endif #if (LUA_VERSION_NUM == 501) #define luaL_setmetatable(L, tname) luaL_getmetatable(L, tname); lua_setmetatable(L, -2) #endif #if (LUA_VERSION_NUM < 504) #define luaL_pushfail lua_pushnil #endif /* * Structure to keep state for each type of API */ typedef struct Lpoll_state { int processed; #ifdef USE_EPOLL int epoll_fd; struct epoll_event events[MAX_EVENTS]; #else fd_set wantread; fd_set wantwrite; fd_set readable; fd_set writable; fd_set all; fd_set err; #endif } Lpoll_state; /* * Add an FD to be watched */ static int Ladd(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); int fd = luaL_checkinteger(L, 2); int wantread = lua_toboolean(L, 3); int wantwrite = lua_toboolean(L, 4); if(fd < 0) { luaL_pushfail(L); lua_pushstring(L, strerror(EBADF)); lua_pushinteger(L, EBADF); return 3; } #ifdef USE_EPOLL struct epoll_event event; event.data.fd = fd; event.events = (wantread ? EPOLLIN : 0) | (wantwrite ? EPOLLOUT : 0); event.events |= EPOLLERR | EPOLLHUP | EPOLLRDHUP; int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_ADD, fd, &event); if(ret < 0) { ret = errno; luaL_pushfail(L); lua_pushstring(L, strerror(ret)); lua_pushinteger(L, ret); return 3; } lua_pushboolean(L, 1); return 1; #else if(fd > FD_SETSIZE) { luaL_pushfail(L); lua_pushstring(L, strerror(EBADF)); lua_pushinteger(L, EBADF); return 3; } if(FD_ISSET(fd, &state->all)) { luaL_pushfail(L); lua_pushstring(L, strerror(EEXIST)); lua_pushinteger(L, EEXIST); return 3; } FD_CLR(fd, &state->readable); FD_CLR(fd, &state->writable); FD_CLR(fd, &state->err); FD_SET(fd, &state->all); if(wantread) { FD_SET(fd, &state->wantread); } else { FD_CLR(fd, &state->wantread); } if(wantwrite) { FD_SET(fd, &state->wantwrite); } else { FD_CLR(fd, &state->wantwrite); } lua_pushboolean(L, 1); return 1; #endif } /* * Set events to watch for, readable and/or writable */ static int Lset(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); int fd = luaL_checkinteger(L, 2); #ifdef USE_EPOLL int wantread = lua_toboolean(L, 3); int wantwrite = lua_toboolean(L, 4); struct epoll_event event; event.data.fd = fd; event.events = (wantread ? EPOLLIN : 0) | (wantwrite ? EPOLLOUT : 0); event.events |= EPOLLERR | EPOLLHUP | EPOLLRDHUP; int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_MOD, fd, &event); if(ret == 0) { lua_pushboolean(L, 1); return 1; } else { ret = errno; luaL_pushfail(L); lua_pushstring(L, strerror(ret)); lua_pushinteger(L, ret); return 3; } #else if(!FD_ISSET(fd, &state->all)) { luaL_pushfail(L); lua_pushstring(L, strerror(ENOENT)); lua_pushinteger(L, ENOENT); return 3; } if(!lua_isnoneornil(L, 3)) { if(lua_toboolean(L, 3)) { FD_SET(fd, &state->wantread); } else { FD_CLR(fd, &state->wantread); } } if(!lua_isnoneornil(L, 4)) { if(lua_toboolean(L, 4)) { FD_SET(fd, &state->wantwrite); } else { FD_CLR(fd, &state->wantwrite); } } lua_pushboolean(L, 1); return 1; #endif } /* * Remove FDs */ static int Ldel(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); int fd = luaL_checkinteger(L, 2); #ifdef USE_EPOLL struct epoll_event event; event.data.fd = fd; int ret = epoll_ctl(state->epoll_fd, EPOLL_CTL_DEL, fd, &event); if(ret == 0) { lua_pushboolean(L, 1); return 1; } else { ret = errno; luaL_pushfail(L); lua_pushstring(L, strerror(ret)); lua_pushinteger(L, ret); return 3; } #else if(!FD_ISSET(fd, &state->all)) { luaL_pushfail(L); lua_pushstring(L, strerror(ENOENT)); lua_pushinteger(L, ENOENT); return 3; } FD_CLR(fd, &state->wantread); FD_CLR(fd, &state->wantwrite); FD_CLR(fd, &state->readable); FD_CLR(fd, &state->writable); FD_CLR(fd, &state->all); FD_CLR(fd, &state->err); lua_pushboolean(L, 1); return 1; #endif } /* * Check previously manipulated event state for FDs ready for reading or writing */ static int Lpushevent(lua_State *L, struct Lpoll_state *state) { #ifdef USE_EPOLL if(state->processed > 0) { state->processed--; struct epoll_event event = state->events[state->processed]; lua_pushinteger(L, event.data.fd); lua_pushboolean(L, event.events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR)); lua_pushboolean(L, event.events & EPOLLOUT); return 3; } #else for(int fd = state->processed + 1; fd < FD_SETSIZE; fd++) { if(FD_ISSET(fd, &state->readable) || FD_ISSET(fd, &state->writable) || FD_ISSET(fd, &state->err)) { lua_pushinteger(L, fd); lua_pushboolean(L, FD_ISSET(fd, &state->readable) | FD_ISSET(fd, &state->err)); lua_pushboolean(L, FD_ISSET(fd, &state->writable)); FD_CLR(fd, &state->readable); FD_CLR(fd, &state->writable); FD_CLR(fd, &state->err); state->processed = fd; return 3; } } #endif return 0; } /* * Wait for event */ static int Lwait(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); int ret = Lpushevent(L, state); if(ret != 0) { return ret; } lua_Number timeout = luaL_checknumber(L, 2); luaL_argcheck(L, timeout >= 0, 1, "positive number expected"); #ifdef USE_EPOLL ret = epoll_wait(state->epoll_fd, state->events, MAX_EVENTS, timeout * 1000); #else /* * select(2) mutates the fd_sets passed to it so in order to not * have to recreate it manually every time a copy is made. */ memcpy(&state->readable, &state->wantread, sizeof(fd_set)); memcpy(&state->writable, &state->wantwrite, sizeof(fd_set)); memcpy(&state->err, &state->all, sizeof(fd_set)); struct timeval tv; tv.tv_sec = (time_t)timeout; tv.tv_usec = ((suseconds_t)(timeout * 1000000)) % 1000000; ret = select(FD_SETSIZE, &state->readable, &state->writable, &state->err, &tv); #endif if(ret == 0) { /* Is this an error? */ lua_pushnil(L); lua_pushstring(L, "timeout"); return 2; } else if(ret < 0 && errno == EINTR) { /* Is this an error? */ lua_pushnil(L); lua_pushstring(L, "signal"); return 2; } else if(ret < 0) { ret = errno; luaL_pushfail(L); lua_pushstring(L, strerror(ret)); lua_pushinteger(L, ret); return 3; } /* * Search for the first ready FD and return it */ #ifdef USE_EPOLL state->processed = ret; #else state->processed = -1; #endif return Lpushevent(L, state); } #ifdef USE_EPOLL /* * Return Epoll FD */ static int Lgetfd(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); lua_pushinteger(L, state->epoll_fd); return 1; } /* * Close epoll FD */ static int Lgc(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); if(state->epoll_fd == -1) { return 0; } if(close(state->epoll_fd) == 0) { state->epoll_fd = -1; } else { lua_pushstring(L, strerror(errno)); lua_error(L); } return 0; } #endif /* * String representation */ static int Ltos(lua_State *L) { struct Lpoll_state *state = luaL_checkudata(L, 1, STATE_MT); lua_pushfstring(L, "%s: %p", STATE_MT, state); return 1; } /* * Create a new context */ static int Lnew(lua_State *L) { /* Allocate state */ Lpoll_state *state = lua_newuserdata(L, sizeof(Lpoll_state)); luaL_setmetatable(L, STATE_MT); /* Initialize state */ #ifdef USE_EPOLL state->epoll_fd = -1; state->processed = 0; int epoll_fd = epoll_create1(EPOLL_CLOEXEC); if(epoll_fd <= 0) { luaL_pushfail(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return 3; } state->epoll_fd = epoll_fd; #else FD_ZERO(&state->wantread); FD_ZERO(&state->wantwrite); FD_ZERO(&state->readable); FD_ZERO(&state->writable); FD_ZERO(&state->all); FD_ZERO(&state->err); state->processed = FD_SETSIZE; #endif return 1; } /* * Open library */ int luaopen_util_poll(lua_State *L) { #if (LUA_VERSION_NUM > 501) luaL_checkversion(L); #endif luaL_newmetatable(L, STATE_MT); { lua_pushliteral(L, STATE_MT); lua_setfield(L, -2, "__name"); lua_pushcfunction(L, Ltos); lua_setfield(L, -2, "__tostring"); lua_createtable(L, 0, 2); { lua_pushcfunction(L, Ladd); lua_setfield(L, -2, "add"); lua_pushcfunction(L, Lset); lua_setfield(L, -2, "set"); lua_pushcfunction(L, Ldel); lua_setfield(L, -2, "del"); lua_pushcfunction(L, Lwait); lua_setfield(L, -2, "wait"); #ifdef USE_EPOLL lua_pushcfunction(L, Lgetfd); lua_setfield(L, -2, "getfd"); #endif } lua_setfield(L, -2, "__index"); #ifdef USE_EPOLL lua_pushcfunction(L, Lgc); lua_setfield(L, -2, "__gc"); #endif } lua_createtable(L, 0, 3); { lua_pushcfunction(L, Lnew); lua_setfield(L, -2, "new"); #define push_errno(named_error) lua_pushinteger(L, named_error);\ lua_setfield(L, -2, #named_error); push_errno(EEXIST); push_errno(ENOENT); } return 1; }