Blame ext/posix/poll.c

Packit 437b5e
/*
Packit 437b5e
 * POSIX library for Lua 5.1, 5.2 & 5.3.
Packit 437b5e
 * (c) Gary V. Vaughan <gary@vaughan.pe>, 2013-2015
Packit 437b5e
 * (c) Reuben Thomas <rrt@sc3d.org> 2010-2013
Packit 437b5e
 * (c) Natanael Copa <natanael.copa@gmail.com> 2008-2010
Packit 437b5e
 * Clean up and bug fixes by Leo Razoumov <slonik.az@gmail.com> 2006-10-11
Packit 437b5e
 * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> 07 Apr 2006 23:17:49
Packit 437b5e
 * Based on original by Claudio Terra for Lua 3.x.
Packit 437b5e
 * With contributions by Roberto Ierusalimschy.
Packit 437b5e
 * With documentation from Steve Donovan 2012
Packit 437b5e
 */
Packit 437b5e
/***
Packit 437b5e
 Synchronous I/O Multiplexing.
Packit 437b5e
Packit 437b5e
 Examine file descriptors for events, such as readyness for I/O.
Packit 437b5e
Packit 437b5e
@module posix.poll
Packit 437b5e
*/
Packit 437b5e
Packit 437b5e
#include <config.h>
Packit 437b5e
Packit 437b5e
#include <poll.h>
Packit 437b5e
Packit 437b5e
#include "_helpers.c"
Packit 437b5e
Packit 437b5e
Packit 437b5e
static struct {
Packit 437b5e
	short       bit;
Packit 437b5e
	const char *name;
Packit 437b5e
} poll_event_map[] = {
Packit 437b5e
#define MAP(_NAME) \
Packit 437b5e
	{POLL##_NAME, #_NAME}
Packit 437b5e
	MAP(IN),
Packit 437b5e
	MAP(PRI),
Packit 437b5e
	MAP(OUT),
Packit 437b5e
	MAP(ERR),
Packit 437b5e
	MAP(HUP),
Packit 437b5e
	MAP(NVAL),
Packit 437b5e
#undef MAP
Packit 437b5e
};
Packit 437b5e
Packit 437b5e
#define PPOLL_EVENT_NUM (sizeof(poll_event_map) / sizeof(*poll_event_map))
Packit 437b5e
Packit 437b5e
static void
Packit 437b5e
poll_events_createtable(lua_State *L)
Packit 437b5e
{
Packit 437b5e
	lua_createtable(L, 0, PPOLL_EVENT_NUM);
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
static short
Packit 437b5e
poll_events_from_table(lua_State *L, int table)
Packit 437b5e
{
Packit 437b5e
	short events = 0;
Packit 437b5e
	size_t i;
Packit 437b5e
Packit 437b5e
	/* Convert to absolute index */
Packit 437b5e
	if (table < 0)
Packit 437b5e
		table = lua_gettop(L) + table + 1;
Packit 437b5e
Packit 437b5e
	for (i = 0; i < PPOLL_EVENT_NUM; i++)
Packit 437b5e
	{
Packit 437b5e
		lua_getfield(L, table, poll_event_map[i].name);
Packit 437b5e
		if (lua_toboolean(L, -1))
Packit 437b5e
			events |= poll_event_map[i].bit;
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
	}
Packit 437b5e
Packit 437b5e
	return events;
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
static void
Packit 437b5e
poll_events_to_table(lua_State *L, int table, short events)
Packit 437b5e
{
Packit 437b5e
	size_t i;
Packit 437b5e
Packit 437b5e
	/* Convert to absolute index */
Packit 437b5e
	if (table < 0)
Packit 437b5e
		table = lua_gettop(L) + table + 1;
Packit 437b5e
Packit 437b5e
	for (i = 0; i < PPOLL_EVENT_NUM; i++)
Packit 437b5e
	{
Packit 437b5e
		lua_pushboolean(L, events & poll_event_map[i].bit);
Packit 437b5e
		lua_setfield(L, table, poll_event_map[i].name);
Packit 437b5e
	}
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
static nfds_t
Packit 437b5e
poll_fd_list_check_table(lua_State *L, int table)
Packit 437b5e
{
Packit 437b5e
	nfds_t fd_num = 0;
Packit 437b5e
Packit 437b5e
	/*
Packit 437b5e
	 * Assume table is an argument number.
Packit 437b5e
	 * Should be an assert(table > 0).
Packit 437b5e
	 */
Packit 437b5e
Packit 437b5e
	luaL_checktype(L, table, LUA_TTABLE);
Packit 437b5e
Packit 437b5e
	/* Nil key - the one before first */
Packit 437b5e
	lua_pushnil(L);
Packit 437b5e
Packit 437b5e
	/* Push each key/value pair, popping previous key */
Packit 437b5e
	while (lua_next(L, 1) != 0)
Packit 437b5e
	{
Packit 437b5e
		/* Verify the fd key */
Packit 437b5e
		luaL_argcheck(L, lua_isinteger(L, -2), table,
Packit 437b5e
					  "contains non-integer key(s)");
Packit 437b5e
Packit 437b5e
		/* Verify the table value */
Packit 437b5e
		luaL_argcheck(L, lua_istable(L, -1), table,
Packit 437b5e
					  "contains non-table value(s)");
Packit 437b5e
		lua_getfield(L, -1, "events");
Packit 437b5e
		luaL_argcheck(L, lua_istable(L, -1), table,
Packit 437b5e
					  "contains invalid value table(s)");
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
		lua_getfield(L, -1, "revents");
Packit 437b5e
		luaL_argcheck(L, lua_isnil(L, -1) || lua_istable(L, -1), table,
Packit 437b5e
					  "contains invalid value table(s)");
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Remove value (but leave the key) */
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Count the fds */
Packit 437b5e
		fd_num++;
Packit 437b5e
	}
Packit 437b5e
Packit 437b5e
	return fd_num;
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
static void
Packit 437b5e
poll_fd_list_from_table(lua_State *L, int table, struct pollfd *fd_list)
Packit 437b5e
{
Packit 437b5e
	struct pollfd *pollfd = fd_list;
Packit 437b5e
Packit 437b5e
	/*
Packit 437b5e
	 * Assume the table didn't change since
Packit 437b5e
	 * the call to poll_fd_list_check_table
Packit 437b5e
	 */
Packit 437b5e
Packit 437b5e
	/* Convert to absolute index */
Packit 437b5e
	if (table < 0)
Packit 437b5e
		table = lua_gettop(L) + table + 1;
Packit 437b5e
Packit 437b5e
	/* Nil key - the one before first */
Packit 437b5e
	lua_pushnil(L);
Packit 437b5e
Packit 437b5e
	/* Push each key/value pair, popping previous key */
Packit 437b5e
	while (lua_next(L, table) != 0)
Packit 437b5e
	{
Packit 437b5e
		/* Transfer the fd key */
Packit 437b5e
		pollfd->fd = lua_tointeger(L, -2);
Packit 437b5e
Packit 437b5e
		/* Transfer "events" field from the value */
Packit 437b5e
		lua_getfield(L, -1, "events");
Packit 437b5e
		pollfd->events = poll_events_from_table(L, -1);
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Remove value (but leave the key) */
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Proceed to next fd */
Packit 437b5e
		pollfd++;
Packit 437b5e
	}
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
static void
Packit 437b5e
poll_fd_list_to_table(lua_State *L, int table, const struct pollfd *fd_list)
Packit 437b5e
{
Packit 437b5e
	const struct pollfd *pollfd = fd_list;
Packit 437b5e
Packit 437b5e
	/*
Packit 437b5e
	 * Assume the table didn't change since
Packit 437b5e
	 * the call to poll_fd_list_check_table.
Packit 437b5e
	 */
Packit 437b5e
Packit 437b5e
	/* Convert to absolute index */
Packit 437b5e
	if (table < 0)
Packit 437b5e
		table = lua_gettop(L) + table + 1;
Packit 437b5e
Packit 437b5e
	/* Nil key - the one before first */
Packit 437b5e
	lua_pushnil(L);
Packit 437b5e
Packit 437b5e
	/* Push each key/value pair, popping previous key */
Packit 437b5e
	while (lua_next(L, 1) != 0)
Packit 437b5e
	{
Packit 437b5e
		/* Transfer "revents" field to the value */
Packit 437b5e
		lua_getfield(L, -1, "revents");
Packit 437b5e
		if (lua_isnil(L, -1))
Packit 437b5e
		{
Packit 437b5e
			lua_pop(L, 1);
Packit 437b5e
			poll_events_createtable(L);
Packit 437b5e
			lua_pushvalue(L, -1);
Packit 437b5e
			lua_setfield(L, -3, "revents");
Packit 437b5e
		}
Packit 437b5e
		poll_events_to_table(L, -1, pollfd->revents);
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Remove value (but leave the key) */
Packit 437b5e
		lua_pop(L, 1);
Packit 437b5e
Packit 437b5e
		/* Proceed to next fd */
Packit 437b5e
		pollfd++;
Packit 437b5e
	}
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
Packit 437b5e
/***
Packit 437b5e
Wait for events on multiple file descriptors.
Packit 437b5e
@function poll
Packit 437b5e
@tparam table fds list of file descriptors
Packit 437b5e
@int[opt=-1] timeout maximum timeout in milliseconds, or -1 to block indefinitely
Packit 437b5e
@treturn[1] int 0 if timed out, 1 if *fd* is ready
Packit 437b5e
@return[2] nil
Packit 437b5e
@treturn[2] string error message
Packit 437b5e
@treturn[2] int errnum
Packit 437b5e
@see poll(2)
Packit 437b5e
@see rpoll
Packit 437b5e
@see poll.lua
Packit 437b5e
*/
Packit 437b5e
static int
Packit 437b5e
Ppoll(lua_State *L)
Packit 437b5e
{
Packit 437b5e
	struct pollfd *fd_list, static_fd_list[16];
Packit 437b5e
	nfds_t fd_num = poll_fd_list_check_table(L, 1);
Packit 437b5e
	int r, timeout = optint(L, 2, -1);
Packit 437b5e
	checknargs(L, 2);
Packit 437b5e
Packit 437b5e
	fd_list = (fd_num <= sizeof(static_fd_list) / sizeof(*static_fd_list))
Packit 437b5e
					? static_fd_list
Packit 437b5e
					: lua_newuserdata(L, sizeof(*fd_list) * fd_num);
Packit 437b5e
Packit 437b5e
Packit 437b5e
	poll_fd_list_from_table(L, 1, fd_list);
Packit 437b5e
Packit 437b5e
	r = poll(fd_list, fd_num, timeout);
Packit 437b5e
Packit 437b5e
	/* If any of the descriptors changed state */
Packit 437b5e
	if (r > 0)
Packit 437b5e
		poll_fd_list_to_table(L, 1, fd_list);
Packit 437b5e
Packit 437b5e
	return pushresult(L, r, NULL);
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
Packit 437b5e
/***
Packit 437b5e
Wait for some event on a file descriptor.
Packit 437b5e
Based on [http://lua-users.org/lists/lua-l/2007-11/msg00346.html]().
Packit 437b5e
@function rpoll
Packit 437b5e
@int fd file descriptor
Packit 437b5e
@int[opt=-1] timeout maximum timeout in milliseconds, or -1 to block indefinitely
Packit 437b5e
@treturn[1] int 0 if timed out, 1 if *fd* is ready
Packit 437b5e
@return[2] nil
Packit 437b5e
@return[2] error message
Packit 437b5e
@treturn[2] int errnum
Packit 437b5e
@see poll
Packit 437b5e
@usage
Packit 437b5e
fh = io.open "one"
Packit 437b5e
while true do
Packit 437b5e
  r = rpoll (fh, 500)
Packit 437b5e
  if r == 0 then
Packit 437b5e
    print 'timeout'
Packit 437b5e
  elseif r == 1 then
Packit 437b5e
    print (fh:read ())
Packit 437b5e
  else
Packit 437b5e
    print "finish!"
Packit 437b5e
    break
Packit 437b5e
  end
Packit 437b5e
end
Packit 437b5e
*/
Packit 437b5e
static int
Packit 437b5e
Prpoll(lua_State *L)
Packit 437b5e
{
Packit 437b5e
	struct pollfd fds;
Packit 437b5e
	int file = checkint(L, 1);
Packit 437b5e
	int timeout = checkint(L, 2);
Packit 437b5e
	checknargs(L, 2);
Packit 437b5e
	fds.fd = file;
Packit 437b5e
	fds.events = POLLIN;
Packit 437b5e
	return pushresult(L, poll(&fds, 1, timeout), NULL);
Packit 437b5e
}
Packit 437b5e
Packit 437b5e
Packit 437b5e
static const luaL_Reg posix_poll_fns[] =
Packit 437b5e
{
Packit 437b5e
	LPOSIX_FUNC( Ppoll		),
Packit 437b5e
	LPOSIX_FUNC( Prpoll		),
Packit 437b5e
	{NULL, NULL}
Packit 437b5e
};
Packit 437b5e
Packit 437b5e
Packit 437b5e
LUALIB_API int
Packit 437b5e
luaopen_posix_poll(lua_State *L)
Packit 437b5e
{
Packit 437b5e
	luaL_register(L, "posix.poll", posix_poll_fns);
Packit 437b5e
	lua_pushliteral(L, "posix.poll for " LUA_VERSION " / " PACKAGE_STRING);
Packit 437b5e
	lua_setfield(L, -2, "version");
Packit 437b5e
Packit 437b5e
	return 1;
Packit 437b5e
}