Multi threading with Lua

Hi,

I want to create asynchronous HTTP requests and still be able to use Lua to generate scenes.
However, all of my scenes call my APIs on server to receive data, which is then shown on screen.

I want to use pthreads & libcurl easy interface to deal with http requests and then perform a callback to lua, unfortunately, the lua_State used by lua to call a cfunction is closed by the time the callback is made, so I have to open a new lua_State.
If I open new lua state I cannot access the object, to which I want to send the response.
Maybe I’m wrong, but it is possible to have something like this:

function decorate(obj) 

  [create everything on the scene]

  function obj:callback(xmlString)
    [update the scene using data received from server]
  end

  function obj:httpRequest()
    local function callback(xmlString) self:callback(xmlString) end
    sendHttpRequestUsingCFunction(url, callback)
  end

  obj:httpRequest()

end

So I will create a scene and call a cfunction passing a pointer to a lua function. CFunction will create a new thread and return. In new thread I will call my server, receive xml and call the callback function passing the xml string.

How can I achieve this?

Thanks

1 Like

download llthreads from https://github.com/dualface/cocos2d-x/tree/master/lua/exts
integrating llthreads to your project, reference http://www.cocos2d-x.org/boards/11/topics/6348

create network.lua

module("network", package.seeall)
local _llthreads = require("llthreads")
local _url       = require("socket.url")
local _http      = require("socket.http")
local _ltn12     = require("ltn12")
local _mime      = require("mime")

--[[ generate URL-encoded query string
syntax:
    network.http_build_query(params)

examples:
    local query = network.http_build_query({
        title = "Title",
        message = "Hello"
    })
--]]
function http_build_query(params)
    local query = ""
    for k, v in pairs(params) do
        query = query..string.format("%s=%s&", k, _url.escape(v))
    end
    return string.sub(query, 1, string.len(query) - 1)
end


local request_thread_code = [[
    local _http  = require("socket.http")
    local _ltn12 = require("ltn12")

    local args = ...
    local url = args.url
    local method = args.method
    local response = {}

    _http.TIMEOUT = args.timeout or 10

    local requestParams = {
        url    = url,
        method = method,
        sink   = _ltn12.sink.table(response)
    }
    local params = args.params
    if type(params) == "table" then
        if params.body then
            requestParams.source = _ltn12.source.string(tostring(params.body))
        end
        if params.headers then
            requestParams.headers = params.headers
        end
    end

    local result, code, headers = _http.request(requestParams)
    return result, code, headers, table.concat(response)
]]

-- makes an asynchronous HTTP or HTTPS request to a URL
function request(url, method, listener, params)
    local thread
    local function onComplete(result, code, headers, response)
        if result then
            -- succeed
            listener(true, code, headers, response)
        else
            -- failed
            listener(false, code) -- code is error message
        end
        thread = nil -- reference to thread object, avoid gc call join()
    end

    if type(params) == "table" and type(params.body) == "table" then
        params.body = http_build_query(params.body)
    end

    thread = _llthreads.new(request_thread_code, onComplete, {
        url = url,
        method = method,
        timeout = 3,
        params = params
    })
    thread:start()
end

How to use:

require("network")

local function onRequestCompleted(ok, code, headers, response)
    if not ok then
        -- request failed, code is error code
        return
    end

    -- response is remote data, HTML, JSON string, etc..
end


local query = "http://www.mysite.com/abc.php"
local params = {
    body = {  -- POST data
        key         = "111",
        key2        = "222"
    }
}
network.request(query, "POST", onRequestCompleted, params)

good luck :slight_smile:

O_O

I’m shocked [again] by completeness of your answer!
Thank you very much!

I am only wondering if you have ever tried to run this on Android?
I will have to update some of the make files probably.

I use these code in my iOS game, Android not tried.

very good example, marked!

After integrating the llthreads into cocos2d-x 3.16. I can start a new thread via llthreads.new(…). But in the thread, I could not require some modules. I saw “module ‘xxxx’ not found” message.
These modules can be loaded successfully in the cocos2d-x main thread.