The deadlock was a usage error.
A better title would be: Naively switching to Java virtual threads caused a deadlock in TPC-C for Progress SQL.
The deadlock was a usage error.
A better title would be: Naively switching to Java virtual threads caused a deadlock in TPC-C for Progress SQL.
"Please use the original title, unless it is misleading or linkbait; don't editorialize." - https://news.ycombinator.com/newsguidelines.html
It really wasn't. There were people on here, including Oracle employees, claiming that the virtual thread implementation was a drop-in replacement that would work (not necessarily perform better, but work) in all cases.
I don't know if those Oracle employees actually did outright say -- or even imply -- "in all cases" as the GP asserted, but if they did, then "only" working in "common usage scenarios" would definitely be overselling the feature.
This was a known and advertised failure case with the new threads runtime, exacerbated by limitations in the implementation that cause certain blocking operations to block the current OS thread instead of blocking the virtual thread and allowing another virtual thread to be re-use the underlying OS thread.
My understanding is there is work to make synchronized not pin the carrier thread, but that's some pretty complex and important code to change.
It definitely leaves room to optimize by not pinning that thread, which would be great, but that shouldn't change semantics at all. Or is there something actually screwed up in the implementation of virtual threads that makes this a much bigger issue?
Been away from Java land for a while. How did something like that even get into release? That’s like a pretty big loaded shotgun to leave lying around with lots of kids playing, no?
Also, this is a benchmark. It's not surprising that they managed to produce a situation where more than n_cores virtual threads would actually start waiting.
So I don’t see the big fuss about it - don’t spawn a million virtual thread that all just spams synchronized?
If they're, like, limiting to CPU cores * 2 threads: yeah that would be Bad™. Unambiguously. I haven't been able to find anything conclusive about this though.
As mentioned in another comment: jdk.virtualThreadScheduler.maxPoolSize
That would indeed be a problem if it's not similarly unlimited by default. Configurable makes perfect sense, as does attempting to be conservative, but small hard-capped defaults are very obviously going to cause problems, especially while synchronized locks the carrier.
>The maximum number of platform threads available to the scheduler. It defaults to 256.
Yeah, that's pretty small. >256 simultaneous synchronized calls doesn't seem particularly extreme, given how common its use is.
Tho now I wonder if you can just set this to max-int and resume like normal, or if giant values do awful things internally...
The issue is that Object.wait doesn't suspend virtual threads, so you get deadlocks. The answer is to reimplement usages of the wait/notify pattern to use locks or concurrent collections (for example, using a concurrent message queue for the producer/consumer pattern, which is a common use case for synchronized/wait/notify).
https://docs.oracle.com/en/java/javase/21/core/virtual-threa...
> Pinning does not make an application incorrect, but it might hinder its scalability.
The documentation is wrong.
> The problem is that this synchronized code might be deeply embedded within the libraries you use. In our case, it was within the c3p0 library. So, the fix is straightforward: we simply wrapped the connection with a java.util.concurrent.Semaphore.
I bet if you just checked out connections and slept a random amount of time you’d have the same problem.
Virtual threads changed the contract a little bit. Now one virtual thread running certain code can prevent a different virtual thread from ever getting any cycles even though they are not dependent on each other in the Java code. It’s a side effect of the current Java implementation.
The rules changed, and it tripped up c3p0. Unless they explicitly said somewhere that they were completely ready for virtual threads I’m not sure anyone is at fault here.