Skip to content

Fix array shifting bug when stopping tweens inside callbacks#16

Open
nanodesuologist wants to merge 2 commits into
rxi:masterfrom
nanodesuologist:master
Open

Fix array shifting bug when stopping tweens inside callbacks#16
nanodesuologist wants to merge 2 commits into
rxi:masterfrom
nanodesuologist:master

Conversation

@nanodesuologist

Copy link
Copy Markdown

Problem

Calling tween:stop() from inside a callback (like onstart) can cause crashes or duplicate tween executions.
flux:remove uses a swap-and-pop optimization for array deletion. If the currently processing tween happens to be at the end of the array, stopping an older tween will teleport the current tween to a different index to fill the gap. However, the loop in flux:update still attempts to remove the current tween using its old array index (i), removing the wrong tween and leaving the current one in the array to execute a second time.

Solution

In flux:update, change flux.remove(self, i) to flux.remove(self, t). This ensures the currently finishing tween is deleted by reference, regardless of whether its index shifted during the frame's execution.

Minimal Reproducible Example

Using the original flux.lua, this script crashes. With the patch, it executes successfully.

local flux = require("flux")

local dummy_target = { x = 0 }
local real_target = { x = 0 }

-- Tween 1: A long-running tween (Index 1)
local tween1 = flux.to(dummy_target, 10, { x = 100 })

-- Tween 2: A 0-duration tween added at the end of the array (Index 2)
local tween2 = flux.to(real_target, 0, { x = 100 })
    :onstart(function()
        print("Tween 2 started! Stopping Tween 1...")
        tween1:stop() -- This shifts Tween 2 to Index 1
    end)
    :oncomplete(function()
        print("Tween 2 completed!")
    end)

-- flux:update loops backwards. It evaluates Tween 2 at index 2 first.
-- The onstart callback removes Tween 1, swapping Tween 2 into index 1.
-- The step finishes and calls `flux.remove(self, 2)`.
-- CRASH: self[2] is now nil.
flux.update(0.1) 
flux.update(0.1)

@nanodesuologist

Copy link
Copy Markdown
Author

Optimized also. The implemented fix uses a fast-path/slow-path approach. It verifies if the tween is still at index i. If it is (the 99.9% case), it uses the fast O(1) index removal. If the array shifted during execution, it safely falls back to the O(N) reference removal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant