Search code examples
lualuasocketlua-lanes

LuaLanes and LuaSockets


I'm working on a little Lua app (under Lua for Windows, if that matters) that uses sockets to communicate with the outside world. (LuaSocket)

And I'm trying to make several requests in parallel. So I thought LuaLanes was the way to go. (I'm open to alternatives, of course, if there's a better solution, but would prefer not to deal with coroutines for this.)

Something like this:

server = assert (socket.bind ('*', 1234))
client = server : accept ()
-- set id to some unique value
allClients [id] = client
theLane = lanes.gen ("", laneTest) ( id )
print (theLane [1])

Where the laneTest function is defined as follows:

function laneTest (id)
    local client = allClients [id]
    print ('peer: ', client:getpeername())
end

My problem is that inside the laneTest function, when run as a lane, I get this lovely error message:

attempt to index local 'client' (a userdata value)

(from the line client:getpeername())

So.. I'm not sure what's going on here? Is lanes incompatible with sockets, or am I doing something very wrong?

I guess it could be that the version of lanes that ships with Lua for Windows is ancient (luaforwindows) and doesn't work with sockets, but the latest version might? (Lanes 2.0.4 vs the more recent 3.xx)

I don't really know how to go about updating the version of Lanes I've got, else I would have tried that by now, so. I'd appreciate any advice if that's where I could be heading or there's something more obvious that I've done wrong.

Edit: I went ahead and installed lanes via luarocks, and have the same problem using lanes 3.1.6-1 that's installed as a rock.

Edit 2: Tried this (and still failed):

require ('socket')
require ('lanes')
local allClients = {}

function theLane (id)
    print ('the id:', id) -- correctly prints out the id passed to the function
    local SOCKET = require ('socket')
    local client = allClients [id]
    print ('peer:', client:getpeername())
    client : close ()
end

local server = assert (SOCKET.bind ('*', 1234))
local ip, port = server:getsockname ()
local laneFunc = lanes.gen('', theLane)
local client = server:accept ()
allClients [1] = client
local x = laneFunc (1)
print (x[1])
  1. This fails claiming: attempt to call global 'require' (a nil value)
  2. Removing the require ('socket') line inside the function and retrying also fails saying: attempt to index local 'client' (a userdata value)

I apologize in advance for missing the obvious, but... how do you get sockets to work with lanes?

Edit 3:

Well, I'm editing this in for future reference :)

As far as I can tell, there's no way of using Lanes with Sockets without patching luasockets. See discussion here for more about that; but in short (and as explained in Deco's answer): lanes does not work with userdata. luasocket does not provide any other way of accessing socket/socket info.

I have no desire to patch luasocket so as much as I would have rather used lanes, I'm going go ahead and stick with copas or couroutines.

Thanks all!


Solution

  • Programming Lua has example on non-preemptive multithreading (using coroutines), which you can probably use almost directly. In my opinion coroutines will be a better solution for your use case.

    There is also copas library that describes itself as "a dispatcher based on coroutines that can be used by TCP/IP servers", but you can actually use it for sending requests asynchronously too (using a combination of addthread and step calls).