-
Notifications
You must be signed in to change notification settings - Fork 635
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
Dead Lock when @Serializable + companion object #2947
Comments
Hi, could you please clarify, have you encountered a deadlock or do you assume it exists due to the use of We use the |
When looking at the example code I didn't see a deadlock, although there is likely to be an initialization loop causing uninitialised constants to be visible (lazy breaks such loops). Initialization is synchronized by the JVM though, and should not deadlock. |
@shanshin
This code is only added when the @serializable annotation is present. From this observation, it can be inferred that @serializable involves the use of thread locking. The example code in the above discussion actually led to a deadlock situation. In a scenario where two async tasks are running concurrently on a system with at least two CPU cores, a deadlock occurs during the initialization of the Companion object. |
@pdvrieze In Kotlin, an object class is not initialized at JVM load time but rather at runtime when it is first accessed. In fact, the above code actually caused a deadlock, which resulted in all coroutine threads being blocked. This made it extremely difficult to identify the root cause. Here is my hypothesized scenario for how the deadlock occurs:
This screenshot is from an actual app, showing the code in use. After the deadlock occurred, I paused execution in debug mode and confirmed that all coroutine threads were in a waiting state. Additionally, I verified that the last variable in the Companion object was null. For reference, the variable being null was not caused by hitting a breakpoint at that point. ![]() |
This code does not set locks, it only creates a delegate.
|
That is not quite what happens.
|
@shanshin, @pdvrieze If my analysis of the thread deadlock caused by Here is a summary of the issue I encountered:
coroutineScope {
val taskA = async {
Test2(10) // A
// some code
}
val taskB = async {
Test1(10) // B
// some code
}
taskA.await()
taskB.await()
} This means that the "some code" section in the tasks does not execute. Based on this observation, I concluded that If there is no locking and no deadlock, then why does the "some code" section fail to execute? |
@erkas-c it is hard to tell what causes the issue without being able to reproduce it. Code from the summary section of this issue does not help as it works perfectly fine for anyone who attempted to run it. Perhaps, you can extract a trimmed-down reproducer from your project, so we can debug it and try to find the root cause? Also, have you already tried to diagnose the issue by inspecting thread dumps created when the app hangs or by using a tool like JConsole's Detect deadlock? |
Describe the bug
The above code can cause a deadlock.
Since
@Serializable
usesLazyThreadSafetyMode
, a lock is applied when creating theTest1
,Test2
, andTest3
classes.Executing the above code may result in a deadlock.
If a lock is applied to
Test2
at point A and toTest3
at point B,they will be waiting on
defaultTest2
anddefaultTest3
in the companion object.One way to resolve this issue is to avoid creating
Test*
instances inside the companion object.However, if a developer is unaware of this issue, they may struggle to debug it.
Therefore, I suggest generating an error or warning for this case.
To Reproduce
executing the above code
Expected behavior
generating an error or warning for this case. by compile time
Environment
The text was updated successfully, but these errors were encountered: