Commit Graph

23 Commits

Author SHA1 Message Date
Max Cahill
b6ca4c7740
Merge pull request #53 from idbrii/fix-async-lovejs
async: Skip wrapping with xpcall under lovejs
2022-03-07 10:50:36 +11:00
Max Cahill
840f4ba703
Merge pull request #54 from idbrii/capture-w-func
async: Use function to capture ...
2022-03-07 10:49:01 +11:00
David Briscoe
a030a20985 async: Assert that args is a table; add type_or_nil
If args is valid, assert it's a table.

Provides a clear and immediate error message when you convert
add_timeout() to call().

Also add type_or_nil. Very useful for optional parameters like this one.
Seems clearer than checking the arg first:
    if args then assert:type() end
2022-03-06 00:25:24 -08:00
David Briscoe
98f3630c07 async: Skip wrapping with xpcall under lovejs
See Davidobot/love.js#54.

lovejs crashes love with "attempt to yield across metamethod/C-call
boundary" when you try to yield in a coroutine, so we skip wrapping.

We lose the coroutine-local callstack, but at least it works.

Here's my repro:

    local color = {
        purple = {0.25, 0.09, 0.28, 1},
        white = {0.89, 0.91, 0.90, 1},
    }
    local ball = {
        x = 100,
        y = 100,
        r = 20,
    }
    local S = {}
    local input = {
        vanilla = 'v',
        async = 'c',
        quit = 'escape',
    }

    function love.load()
        S.coro = async()
    end

    local function coro_fn()
        S.async_running = true
        for i=1,100 do
            color.purple[4] = i / 100
            coroutine.yield()
        end
        color.purple[4] = 1
        S.async_running = false
        return true
    end

    function love.update(dt)
        S.coro:update(dt)

        if S.vanilla then
            local success, result = coroutine.resume(S.vanilla)
            if result then
                print("coroutine cleared")
                S.vanilla = nil
            end
        end

        if not S.async_running then
            if love.keyboard.isDown(input.vanilla) then
                S.vanilla = coroutine.create(coro_fn)
                print("raw coroutine started")

            elseif love.keyboard.isDown(input.async) then
                print("Starting async call coroutine from update")
                S.coro:call(coro_fn)

            elseif love.keyboard.isDown(input.quit) then
                love.event.quit()
            end
        end
    end

    function love.draw()
        love.graphics.setColor(color.purple)
        love.graphics.circle("fill", ball.x, ball.y, ball.r)

        love.graphics.setColor(color.white)
        local str = "To start a coroutine:"
        for key,val in pairs(input) do
            str = ("%s\n%s: %s"):format(str, key, val)
        end
        love.graphics.printf(str, 5,5, 200, "left")
    end
2022-03-05 23:44:08 -08:00
David Briscoe
8d41c6d3d1 lint: Fix whitespace issues
Remove excess whitespace and remove related ignores.
2022-03-03 10:17:34 -08:00
David Briscoe
585ba82e57 async: Use function to capture ...
Instead of only capturing 8 temps, pass resume to a function so we can
capture all returned values as ...

I might be missing a reason why you wouldn't do it like this.

Test
    -- coro is an async() updated in love.update
    coro:call(
        function()
            coro.wait(1)

            print("jump")
            self.gravity.y = -900
            coro.wait(1)

            print("fall")
            self.gravity.y = 900
            coroutine.yield "hello"

            return "done"
        end,
        nil,
        function(...)
            print("complete", ...)
        end)
2022-03-03 00:08:19 -08:00
Max Cahill
6ce0dfa523 fixed wrong inequality :) 2021-11-18 15:18:58 +11:00
Max Cahill
774965a508 added async.wait for waiting for a certain period of time inside an async task 2021-11-18 15:15:48 +11:00
Max Cahill
0ebe5b1e9f added async.stall to canonicalise the stalling mechanism for async users 2021-10-08 16:17:18 +11:00
Max Cahill
3cc177a0c0 BREAKING class interface refactor - all classes will need a minor update, see class.lua
tl;dr is that new no longer needs to call init, calling :new() directly in user code is not allowed, properties are copied, metamethods work, and a config table is needed rather than a class to extend from, so use {extends = superclass} if you want a minimal fix
2021-07-15 16:15:27 +10:00
Max Cahill
84197b5216 [added] proper tracebacks to errors in async tasks with xpcall (only works for functions with :call, not coroutines with :add) 2020-07-24 09:15:28 +10:00
Max Cahill
a18691f229 [modified] async stall resume behaviour implemented in terms of a tail call 2020-07-17 22:47:42 +10:00
Max Cahill
9e8793668f [modified] async task error format easier to read - still need to move to embedded xpcall+stacktrace for more than 1 line of context though! 2020-07-15 14:16:55 +10:00
Max Cahill
eba27da544 [added] note to async about external xpcall being needed for better stack traces 2020-06-02 15:17:36 +10:00
Max Cahill
77327aa852 [modified] minor async refactoring
[fixed] missing arguments beyond 1 for async calls
2020-05-19 22:11:38 +10:00
Max Cahill
fc0812d94e [fixed] possible missing args causing issues in async 2020-04-28 17:05:25 +10:00
Max Cahill
9048ee116f [fixed] issues with async calls when missing args, callback, or error callback (eg single anonymous function) 2020-04-28 12:08:53 +10:00
Max Cahill
6386aaf298 [modified] big refactor to localised modules and require("batteries"):export() for global usage; renamed files table to tablex and math to mathx to allow using the library in your module root if you really want to. 2020-04-07 13:49:10 +10:00
Max Cahill
bb35fc2f57 [added] stall support, timers and intervals to async 2020-03-16 20:17:48 +11:00
Max Cahill
ad2fc71e7a [fixed] non-global compatible 2020-03-15 21:22:22 +11:00
Max Cahill
0a1e55b300 [fixed] issued with async 2020-02-01 21:38:00 +11:00
Max Cahill
22acd78e5e [added] async todo 2020-02-01 19:39:51 +11:00
Max Cahill
8a582f8c66 [added] async.lua 2020-01-31 11:55:50 +11:00