aboutsummaryrefslogtreecommitdiff
path: root/src/select.c
blob: 0c14ba6e65ba92b705cf48df7acac4dee57b60b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*=========================================================================*\
* Select implementation
* Global Lua fuctions:
*   select: waits until socket ready
* RCS ID: $Id$
\*=========================================================================*/
#include <string.h>
#include <lua.h>
#include <lauxlib.h>

#include "luasocket.h"
#include "lspriv.h"
#include "lsselect.h"
#include "lsfd.h"

/* auxiliar functions */
static int local_select(lua_State *L);
static int local_getfd(lua_State *L);
static int local_pending(lua_State *L);
static int local_FD_SET(lua_State *L);
static int local_FD_ISSET(lua_State *L);

static int select_lua_select(lua_State *L);

/*-------------------------------------------------------------------------*\
* Marks type as selectable
* Input
*   name: type name
\*-------------------------------------------------------------------------*/
void select_addclass(lua_State *L, cchar *lsclass)
{
    lua_pushstring(L, "luasocket(select)");
    lua_gettable(L, LUA_REGISTRYINDEX);
    lua_pushstring(L, lsclass);
    lua_pushnumber(L, 1);
    lua_settable(L, -3);
    lua_pop(L, 1);
}

void select_open(lua_State *L)
{
    /* push select auxiliar lua function and register
    * select_lua_select with it as an upvalue */
#ifdef LUASOCKET_DEBUG
    lua_dofile(L, "lsselect.lua");
#else
#include "lsselect.loh"
#endif
    lua_getglobal(L, LUASOCKET_LIBNAME);
    lua_pushstring(L, "_select");
    lua_gettable(L, -2);
    lua_pushcclosure(L, select_lua_select, 1);
    priv_newglobal(L, "select");
    lua_pop(L, 1);
    /* create luasocket(select) table */
    lua_pushstring(L, "luasocket(select)");
    lua_newtable(L);
    lua_settable(L, LUA_REGISTRYINDEX);
}

/*-------------------------------------------------------------------------*\
* Waits for a set of sockets until a condition is met or timeout.
* Lua Input: {input}, {output} [, timeout]
*   {input}: table of sockets to be tested for input
*   {output}: table of sockets to be tested for output
*   timeout: maximum amount of time to wait for condition, in seconds
* Lua Returns: {input}, {output}, err
*   {input}: table with sockets ready for input
*   {output}: table with sockets ready for output
*   err: "timeout" or nil
\*-------------------------------------------------------------------------*/
static int select_lua_select(lua_State *L)
{
    fd_set read, write;
    FD_ZERO(&read);
    FD_ZERO(&write);
    /* push select lua auxiliar function */
    lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1);
    /* make sure we have enough arguments (nil is the default) */
    lua_settop(L, 4);
    /* pass FD_SET and manipulation functions */
    lua_boxpointer(L, &read);
    lua_boxpointer(L, &write);
    lua_pushcfunction(L, local_FD_SET);
    lua_pushcfunction(L, local_FD_ISSET);
    /* pass getfd function with selectable table as upvalue */
    lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
    lua_pushcclosure(L, local_getfd, 1);
    /* pass pending function */
    lua_pushstring(L, "luasocket(select)"); lua_gettable(L, LUA_REGISTRYINDEX);
    lua_pushcclosure(L, local_pending, 1);
    /* pass select auxiliar C function */
    lua_pushcfunction(L, local_select);
    /* call select auxiliar lua function */
    lua_call(L, 10, 3);
    return 3;
}

static int local_getfd(lua_State *L)
{
    priv_pushclass(L, 1);
    lua_gettable(L, lua_upvalueindex(1));
    if (!lua_isnil(L, -1)) {
        p_fd sock = (p_fd) lua_touserdata(L, 1);
        lua_pushnumber(L, sock->fd);
    }
    return 1;
}

static int local_pending(lua_State *L)
{
    priv_pushclass(L, 1);
    lua_gettable(L, lua_upvalueindex(1));
    if (!lua_isnil(L, -1)) {
        p_fd sock = (p_fd) lua_touserdata(L, 1);
        if (sock->fd_pending(L, sock)) lua_pushnumber(L, 1);
        else lua_pushnil(L);
    }
    return 1;
}

static int local_select(lua_State *L)
{
    int max_fd = (int) lua_tonumber(L, 1);
    fd_set *read_set = (fd_set *) lua_touserdata(L, 2);
    fd_set *write_set = (fd_set *) lua_touserdata(L, 3);
    int deadline = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000);
    struct timeval tv;
    if (deadline >= 0) {
        tv.tv_sec = deadline / 1000;
        tv.tv_usec = (deadline % 1000) * 1000;
        lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, &tv));
    } else {
        lua_pushnumber(L, select(max_fd, read_set, write_set, NULL, NULL));
    }
    return 1;
}

static int local_FD_SET(lua_State *L)
{
    COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1);
    fd_set *set = (fd_set *) lua_topointer(L, 2);
    if (fd >= 0) FD_SET(fd, set);
    return 0;
}

static int local_FD_ISSET(lua_State *L)
{
    COMPAT_FD fd = (COMPAT_FD) lua_tonumber(L, 1);
    fd_set *set = (fd_set *) lua_topointer(L, 2);
    if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1);
    else lua_pushnil(L);
    return 1;
}