[Bug] Deadlock w/ `Timer` and `Lwjgl3Application`
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 25.4k
- Forks
- 6.5k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 5
Description
Issue details
The situation is as follows:
- Create a Lwjgl3Application, and from that application:
- Kick off a different thread that creates a
Timer, which causes the timer instance to be set and the gdx files instance to be cached. - Swap instances to a different Lwjgl3Application. (
Timerimplies it supports this by checking if it's local version offilesmatches that of the staticGdx.files, which will be important later).
Now, the deadlock can happen like so:
- [Main Thread] Lwjgl3Application.java, loop() is called, which is
synchronizedonlifecycleListeners. This calls render(). - [Thread 2] This thread now tries to create a new timer (for some application-logic-specific reason). This thread synchronizes on
threadLockin the instance() call, and so that lock is now held. When it calls the staticthread()method, because GDX files has changed, it calls dispose(). While in dispose(), it tries to callremoveLifecycleListeneron Lwjgl3Application. We need thelifecycleListenerslock to do so, but that is held by Main Thread. So we wait. - [Main Thread] Within that render() call, something tries to create a Timer. We are stuck waiting for the
threadLocklock, which is held by Thread 2.
Now, each thread is waiting for a lock that the other holds, and is deadlocked.
I was able to get this deadlock to happen in the intellij debugger and used a thread dump to confirm all of this.
Version of libGDX and/or relevant dependencies
1.13.0
Stacktrace
Thread 2:
"Test worker@1" tid=0x1 nid=NA waiting for monitor entry
java.lang.Thread.State: BLOCKED
blocks Timer@26630
blocks Timer@26863
blocks pool-52-thread-2@26876
blocks Timer@27042
blocks Timer@27372
waiting for pool-52-thread-2@26876 to release lock on <0x6b93> (a java.lang.Object)
at com.badlogic.gdx.utils.Timer.instance(Timer.java:38)
at com.badlogic.gdx.utils.Timer.schedule(Timer.java:187)
Thread 1:
"pool-52-thread-2@26876" tid=0x93 nid=NA waiting for monitor entry
java.lang.Thread.State: BLOCKED
blocks Test worker@1
waiting for Test worker@1 to release lock on <0x7a0e> (a com.badlogic.gdx.utils.Array)
at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.removeLifecycleListener(Lwjgl3Application.java:402)
at com.badlogic.gdx.utils.Timer$TimerThread.dispose(Timer.java:367)
at com.badlogic.gdx.utils.Timer.thread(Timer.java:48)
- locked <0x6b93> (a java.lang.Object)
at com.badlogic.gdx.utils.Timer.instance(Timer.java:39)
at com.badlogic.gdx.utils.Timer.schedule(Timer.java:187)
Please select the affected platforms
- Android
- iOS
- HTML/GWT
- Windows
- Linux
- macOS
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading com.badlogic.gdx.utils.Timer.java, especially instance(), thread(), and TimerThread.dispose(), alongside Lwjgl3Application.java loop() and removeLifecycleListener(). Reproduce the described application-switch sequence with the thread dump to confirm the lock cycle. Done means the sequence no longer deadlocks on Windows, Linux, or macOS.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- desktop-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100