Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Timer tasks being run after cancellation. #7393

Merged
merged 4 commits into from May 4, 2024

Conversation

NathanSweet
Copy link
Member

Timer considers a task "executed" when the timer thread posts it via Gdx.app.postRunnable. Code on the app thread may cancel a task that has been posted but not executed yet. It is then very confusing for the app to see a task run after it has been cancelled. To be safe, app code needs to check state for every task execution to make sure it is valid. This can be difficult, eg if a task is cancelled and rescheduled, the app could see both the cancelled and rescheduled execution.

We need a way for Timer to post runnables but still be able to remove them before they execute, if needed. This PR does that by having the timer post a single runnable runPostedTasks whenever there are one or more tasks that need to be executed. runPostedTasks then executes each task in Array<Task> postedTasks. When a task is cancelled, it is removed from postedTasks, solving the "executed after cancel" problem. From the app code perspective, everything should work as it did before.

postedTasks is stored in TimerThread (ie globally) rather than Timer because once a task is no longer scheduled to execute in the future, Task reset is called and timer set to null. It no longer knows about the timer, so wouldn't be able to remove itself from postedTasks on the timer.

New synchronization was added around postedTasks, which is synchronized when running tasks. That means a timer posting a new task must wait while all the postedTasks execute. This is probably OK, as tasks aren't generally long running and a newly added task won't be serviced until next frame anyway. If it were important, we could do like the LWJGL backends that use Array<Runnable> runnables, executedRunnables, synchronizing only long enough to copy the tasks that will run.

This PR also adds more lovely Timer tests.

@NathanSweet
Copy link
Member Author

I deployed this to production for Spine (YOLO) and found when a task is run it could modify postedTasks, so I added copying to another array.

@NathanSweet
Copy link
Member Author

A few thousand Spine users have tested it, seems OK!

@NathanSweet NathanSweet merged commit ceb57dd into libgdx:master May 4, 2024
2 checks passed
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.

None yet

1 participant