On Fri, 16 Jan 2026 17:07:34 GMT, Patricio Chilano Mateo
<[email protected]> wrote:
>> Please review the following patch. This fixes a bug in how we handle state
>> changes for the timed `Object.wait` case in `afterYield`, which can leave a
>> virtual thread stuck in the `BLOCKED` state. It can be triggered by two
>> consecutive calls to timed `Object.wait`, if the first call receives a
>> notification and the second call relies on the timeout task to wake up the
>> thread. I added the full sequence of events that leads to the vthread
>> getting stuck in JBS.
>>
>> The fix is to check for `notified` and attempt to change the state to
>> `BLOCKED` inside the synchronized block. This guarantees that we don't
>> change the state of an already new timed `Object.wait` call.
>>
>> The PR includes a new test which reproduces the issue when run several times
>> in mach5. It's a hybrid of my original repro test and another one created by
>> @AlanBateman.
>>
>> Thanks,
>> Patricio
>
> Patricio Chilano Mateo has updated the pull request incrementally with one
> additional commit since the last revision:
>
> add comment in test
src/java.base/share/classes/java/lang/VirtualThread.java line 641:
> 639: synchronized (timedWaitLock()) {
> 640: byte seqNo = ++timedWaitSeqNo;
> 641: timeoutTask = schedule(() ->
> waitTimeoutExpired(seqNo), timeout, MILLISECONDS);
Just a sidenote, I think it would be nice to see if we could eliminate the need
for the seqNo if we were able to allocate the timeoutTask before submitting it,
then using getAndSet or compareAndExchange to install it in the
timeoutTask-field, and on cancellation we can CAS out it to null and only if we
succeed we have successfully uninstalled it and can cancel it. Not high
priority, but I find it valuable to always have an idea of how to do it
differently. This might also make it possible to avoid having to use the
timedWaitLock.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/29255#discussion_r2701250823