lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2009/11/12 Diego Nehab <diego@tecgraf.puc-rio.br>:
>> A quick test on a friend's mac shows that on OSX the
>> behaviour is again different: on failure select returns,
>> and connect returns nil,"connection failed", while on
>> success select also returns, but connect returns
>> nil,"already connected". Am I hitting some undefined use
>> case of the BSD sockets API ?
>
> This is the expected behavior.  "Already connected" is an
> error message that means there is no error. It is what you
> should get in this case and means connection succeeded.
> The failure you are getting is hitting you so quick that
> select is returning right away, so no problem there either.

If this is the expected behaviour this is weird that Linux is not
behaving that way. On Linux, the second call to connect on a
successful connection returns 1,nil.

> Can you tell me more about the error you get when you try to
> call connect() again on Windows?

If the connection succeeded, I get nil,"already connected". If the
connection failed though, select never returns. If I don't call
select, and just call connect in a loop, I get one nil,"timeout", then
nil,"Operation already in progress" for some time, and then it starts
again with a single nil,"timeout" and many nil,"Operation already in
progress", and so on.

While trying to solve the problems, I realized that according to the
MSDN [1] the normal way to detect that a non-blocking connect failed,
is to pass the socket to select in the third fd_set, exceptfds, the
one that detects errors. After looking at the LuaSocket sources, it
seems that this third set is never used, and not bound by
socket.select. So I wrote the following function, that I named
socket.selectex:

static int global_selectex(lua_State *L) {
    int rtab, wtab, etab, itab, ret, ndirty;
    t_socket max_fd;
    fd_set rset, wset, eset;
    t_timeout tm;
    double t = luaL_optnumber(L, 4, -1);
    FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
    lua_settop(L, 4);
    lua_newtable(L); itab = lua_gettop(L);
    lua_newtable(L); rtab = lua_gettop(L);
    lua_newtable(L); wtab = lua_gettop(L);
    lua_newtable(L); etab = lua_gettop(L);
    max_fd = collect_fd(L, 1, SOCKET_INVALID, itab, &rset);
    ndirty = check_dirty(L, 1, rtab, &rset);
    t = ndirty > 0? 0.0: t;
    timeout_init(&tm, t, -1);
    timeout_markstart(&tm);
    max_fd = collect_fd(L, 2, max_fd, itab, &wset);
    max_fd = collect_fd(L, 3, max_fd, itab, &eset);
    ret = socket_select(max_fd+1, &rset, &wset, &eset, &tm);
    if (ret > 0 || ndirty > 0) {
        return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
        return_fd(L, &wset, max_fd+1, itab, wtab, 0);
        return_fd(L, &eset, max_fd+1, itab, etab, 0);
        make_assoc(L, rtab);
        make_assoc(L, wtab);
        make_assoc(L, etab);
        return 3;
    } else if (ret == 0) {
        lua_pushstring(L, "timeout");
        return 4;
    } else {
        lua_pushstring(L, "error");
        return 4;
    }
}

It takes another array before the timeout, and return another one
before the msg if any. If I put the connecting socket in both write
and error lists, it is returned in either one depending on the
connection result. On success I don't have to call connect again to
use the socket.

I haven't tested it on Linux yet, but I think it should be feasible if
you'd like to.