(LUA) Repeated async HTTP requests makes game crash on exit

neutrinou

New member
Joined
Aug 15, 2024
Messages
1
Reaction score
0
Good morning,

I have problem with my LUA script and I hope someone had similar problem and found the solution.

My script is a part of a bigger mod, but after a lot of test, I found out that my bug is related to a single feature in my mod. Basically, I have a URL which provides data. My LUA script have to fetch it each 20s and check if data has changed or not, then perform some action if it has changed. I'm using the Effil + luarequest function to achieve fetching the URL.

Now, my initial problem was, sometimes the mod crashes randomly with the error "cannot resume non-suspended coroutine" in LUA. I managed to fix this issue by storing all LUA threads inside a table. So instead of:
Code:
lua_thread.create(function()
    do_some_action()
end)
I do:
Code:
local threads_temp = {}
table.insert(threads_temp, lua_thread.create(function()
    do_some_action()
end))
and this seems to solve the random "cannot resume non-suspended coroutine". However, now when I exit the game, I have an exception happening:
Full error reporting is here:
Code:
SA-MP 0.3.7
Exception At Address: 0x6DA577A6
Base: 0x04750000

Registers:
EAX: 0x206976D9    EBX: 0x0177F3CC    ECX: 0xFFFFD8F0    EDX: 0x0000001C
ESI: 0x12C237C0    EDI: 0x1CE0DC14    EBP: 0x0177F3FC    ESP: 0x0177F390
EFLAGS: 0x00010216

Stack:
+0000: 0x12C237C0   0x6DB9EF99   0x12C237C0   0xFFFFD8F0
+0010: 0x0000001C   0x6DBDA750   0x1CE0DC0C   0x12B301C0
+0020: 0x6DBDA7B5   0x1CE0DC14   0x6DBDA750   0x12B302CC
+0030: 0x12B30D58   0x12B70001   0x0BEBE7C0   0xFFFFD8EC
+0040: 0x6DBB8380   0x12B302CC   0x12B30D58   0x12B43340
+0050: 0x6DA7EC80   0x00000001   0x00000001   0x0177F3D0
+0060: 0x0177F428   0x6DC065C0   0xFFFFFFFF   0x12B301C0
+0070: 0x6DA5280D   0x12B301C0   0x12B822D8   0x0177F414
+0080: 0x00000000   0x12B42948   0x00000002   0x12B43340
+0090: 0x00000000   0x00000000   0x0177F4F8   0x6DA741B0
+00A0: 0x00000000   0x12B822A8   0x12B301F0   0x0BEE7E60
+00B0: 0x6DA573BB   0x12B301C0   0x00000000   0x00000000
+00C0: 0x00000008   0x00000001   0x00000002   0x00000001
+00D0: 0x6DACF9E5   0x12B301C0   0x00000002   0xFFFFFFFF
+00E0: 0x00000001   0x12B301C0   0x12B301C0   0x0177F504
+00F0: 0x0BEE7E60   0x6DACD48A   0x0177F4CC   0x0177F55C
+0100: 0x0177F55C   0x00000002   0x0177F4E8   0x01A3A740
+0110: 0x01B48720   0x01A40910   0x0177F540   0x6DA741B0
+0120: 0x12A9C7D0   0x12A70008   0x12B1B508   0x12AAAB50
+0130: 0x0000000A   0x0177F470   0x00000000   0x01A3B380
+0140: 0x00000000   0x12B2FFE8   0x00000000   0x12A71208
+0150: 0x12A70008   0x12A71870   0x0BEE7E68   0x00000001
+0160: 0x00000000   0x00000000   0x0177F53C   0x6DC00AA9
+0170: 0x00000001   0x0177F548   0x6DACB885   0x0BEBE7C0
+0180: 0x0BEE7E70   0x0177F55C   0x0177F57C   0x01A40840
+0190: 0x0BEBE7C0   0x01A3D7C0   0x01A3A740   0x01A40910
+01A0: 0x01B48728   0x01B48720   0x0177F51C   0x0177F564
+01B0: 0x6DC009C0   0x00000000   0x0177F570   0x6DAD2B5D
+01C0: 0x0177F55C   0x0177F57C   0x0BEBE7C0   0x0BEB7058
+01D0: 0x0BEE7C68   0x0177F5AC   0x6DC00F28   0x00000000
+01E0: 0x0177F5B8   0x6DBDC07B   0x0BEBE7C0   0x00000001
+01F0: 0x6DC45F04   0x0BEBE7C0   0x01A3D7C0   0x0177F4D0
+0200: 0x01A3D610   0x01A3D5E0   0x01A3D778   0x01A3D778
+0210: 0x01A3B2A0   0x0BEBE7C0   0x0177F580   0x0177F5C8
+0220: 0x6DC067B4   0x0000000C   0x0177F5D4   0x6DBA4663
+0230: 0x0BEE7C20   0x01B485A0   0x0177F610   0x6DC011F0
+0240: 0xFFFFFFFF   0x6DC45F14   0x6DBA4C69   0x6DC45F04
+0250: 0xFFFFFFFF   0x756B2120   0x0177F61C   0x6DBA5B8B
+0260: 0x030A001F   0x6DBE31B5   0x6F6C6E00   0x6E696461
+0270: 0x2E2E2E67   0x00090000   0x00000000   0x0000000F

SCM Op: 0x215, lDbg: 0 LastRendObj: 3858

Game Version: US 1.0

Here is my current script, cleaned of all other features that seem not being related to the problem:
Code:
script_name("Radio Frequency")
script_authors("Me")
script_version("0.0.1")

require "moonloader"
require "sampfuncs"
local sampev = require "lib.samp.events"

local cjson = require "cjson"
local effil = require "effil"

local freq_refresh_enabled = true
local freq_refresh_cooldown = 20
local freq_requested = false
local freq_last = 0
local freq_can_request = nil
local profile_requested = false

local server_url = "(my server base url)"

local threads = {}
local threads_temp = {}

function asyncHttpRequest(method, url, args, resolve, reject)
    local request_thread = effil.thread(function (method, url, args)
        local requests = require "requests"
        local result, response = pcall(requests.request, method, url, args)
        if result then
            response.json, response.xml = nil, nil
            return true, response
        else
            return false, response
        end
    end)(method, url, args)


    if not resolve then resolve = function() end end
    if not reject then reject = function() end end

    table.insert(threads_temp, lua_thread.create(function()
        local runner = request_thread
        while true do
            local status, err = runner:status()
            if not err then
                if status == 'completed' then
                    local result, response = runner:get()
                    if result then
                        resolve(response)
                    else
                        reject(response)
                    end
                    return
                elseif status == 'canceled' then
                    return reject(status)
                end
            else
                return reject(err)
            end
            wait(0)
        end
    end))
end

function freq_current_process(response)
    freq_requested = false

    if (not response) or (not response.text) then
        sampAddChatMessage("{C0392B}Failed to get current radio frequency. {FFFFFF}No HTTP response found.", 0xFFFFFF)
        return
    end

    local response_json = decodeJson(response.text)
    if not response_json then
        sampAddChatMessage("{C0392B}Failed to get current radio frequency. {FFFFFF}Server output can't be decoded as JSON.", 0xFFFFFF)
        return
    end

    if response_json.message then
        sampAddChatMessage("{C0392B}Failed to get current radio frequency. {FFFFFF}API failed: " .. response.message, 0xFFFFFF)
        return
    end

    if not response_json.data then
        sampAddChatMessage("{C0392B}Failed to get current radio frequency. {FFFFFF}Response data not found.", 0xFFFFFF)
        return
    end

    if freq_last ~= tonumber(response_json.data) then
        sampAddChatMessage("Updating freq from " .. freq_last .. " to " .. response_json.data, 0xFFFFFF)
        freq_last = tonumber(response_json.data)
        table.insert(threads_temp, lua_thread.create(function()
            wait(0)
            sampSendChat("/setfreq " .. data)
        end))
    end
end

function request_profile()
    if profile_requested then
        return
    end

    profile_requested = true
    table.insert(threads_temp, lua_thread.create(function()
        wait(0)
        sampSendChat("/profile")
    end))
end

function cmd_freq_current()
    if freq_requested then
        return
    end

    table.insert(threads_temp, lua_thread.create(function()
        if freq_can_request == nil then
            if not profile_requested then
                request_profile()
            end
            repeat wait(50) until profile_requested == false
        end
       
        if freq_can_request == false then
            sampAddChatMessage("{C0392B}Requesting current frequency failed: permission rejected", 0xFFFFFF)
            return
        end

        local args = {
            headers = {
                ['Accept'] = 'application/json'
            },
            timeout = 5
        }

        freq_requested = true
        asyncHttpRequest("GET", server_url .. "/frequencies/current", args, freq_current_process, function(response)
            freq_refresh_enabled = false
            freq_requested = false
            sampAddChatMessage("{C0392B}Requesting current frequency failed:", 0xFFFFFF)
            sampAddChatMessage(response, 0xFFFFFF)
        end)
    end))
end

function main()
    repeat wait(50) until isSampAvailable()
    repeat wait(50) until string.find(sampGetCurrentServerName(), "MY SERVER NAME GOES HERE")

    sampRegisterChatCommand("gfreq", cmd_freq_current)

    local this_script = thisScript()
    sampAddChatMessage("{AA0000}" .. this_script.name .. " {FFFFFF}v" .. this_script.version .. "", 0xFFFFFF)

    -- Load profile information
    table.insert(threads, lua_thread.create(function()
        wait(5000)
        while true do
            request_profile()
            wait(600000)
        end
    end))

    -- Refresh frequency
    table.insert(threads, lua_thread.create(function()
        while true do
            if freq_refresh_enabled then
                freq_current_init()
                repeat wait(50) until freq_requested == false
                wait(freq_refresh_cooldown * 1000)
            end
            wait(50)
        end
    end))
end

function sampev.onServerMessage(colorid, text)
    local gang = text:match("Gang%: (.+)")
    if gang then
        if gang == "A" then
            freq_can_request = true
        else
            freq_can_request = false
        end

        profile_requested = false
    end

    if text:match("You don't have a portable radio") then
        freq_refresh_enabled = false
    end
end

Is this still related to some threads? Can you help me fix this bug?

Thank you for your help.
 
Top