RFR: 8338383: Implement JEP 491: Synchronize Virtual Threads without Pinning [v7]

David Holmes dholmes at openjdk.org
Thu Oct 24 05:58:18 UTC 2024


On Wed, 23 Oct 2024 20:22:26 GMT, Patricio Chilano Mateo <pchilanomate at openjdk.org> wrote:

>> This is the implementation of JEP 491: Synchronize Virtual Threads without Pinning. See [JEP 491](https://bugs.openjdk.org/browse/JDK-8337395) for further details.
>> 
>> In order to make the code review easier the changes have been split into the following initial 4 commits:
>> 
>> - Changes to allow unmounting a virtual thread that is currently holding monitors.
>> - Changes to allow unmounting a virtual thread blocked on synchronized trying to acquire the monitor.
>> - Changes to allow unmounting a virtual thread blocked in `Object.wait()` and its timed-wait variants.
>> - Changes to tests, JFR pinned event, and other changes in the JDK libraries.
>> 
>> The changes fix pinning issues for all 4 ports that currently implement continuations: x64, aarch64, riscv and ppc. Note: ppc changes were added recently and stand in its own commit after the initial ones.
>> 
>> The changes fix pinning issues when using `LM_LIGHTWEIGHT`, i.e. the default locking mode, (and `LM_MONITOR` which comes for free), but not when using `LM_LEGACY` mode. Note that the `LockingMode` flag has already been deprecated ([JDK-8334299](https://bugs.openjdk.org/browse/JDK-8334299)), with the intention to remove `LM_LEGACY` code in future releases.
>> 
>> 
>> ## Summary of changes
>> 
>> ### Unmount virtual thread while holding monitors
>> 
>> As stated in the JEP, currently when a virtual thread enters a synchronized method or block, the JVM records the virtual thread's carrier platform thread as holding the monitor, not the virtual thread itself. This prevents the virtual thread from being unmounted from its carrier, as ownership information would otherwise go wrong. In order to fix this limitation we will do two things:
>> 
>> - We copy the oops stored in the LockStack of the carrier to the stackChunk when freezing (and clear the LockStack). We copy the oops back to the LockStack of the next carrier when thawing for the first time (and clear them from the stackChunk). Note that we currently assume carriers don't hold monitors while mounting virtual threads.
>> 
>> - For inflated monitors we now record the `java.lang.Thread.tid` of the owner in the ObjectMonitor's `_owner` field instead of a JavaThread*. This allows us to tie the owner of the monitor to a `java.lang.Thread` instance, rather than to a JavaThread which is only created per platform thread. The tid is already a 64 bit field so we can ignore issues of the counter wrapping around.
>> 
>> #### General notes about this part:
>> 
>> - Since virtual th...
>
> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Minor fixes in inc/dec_held_monitor_count on aarch64 and riscv

src/java.base/share/classes/java/lang/Thread.java line 654:

> 652:      * {@link Thread#PRIMORDIAL_TID} +1 as this class cannot be used during
> 653:      * early startup to generate the identifier for the primordial thread. The
> 654:      * counter is off-heap and shared with the VM to allow it assign thread

Suggestion:

     * counter is off-heap and shared with the VM to allow it to assign thread

src/java.base/share/classes/java/lang/Thread.java line 655:

> 653:      * early startup to generate the identifier for the primordial thread. The
> 654:      * counter is off-heap and shared with the VM to allow it assign thread
> 655:      * identifiers to non-Java threads.

Why do non-JavaThreads need an identifier of this kind?

src/java.base/share/classes/java/lang/VirtualThread.java line 631:

> 629:         // Object.wait
> 630:         if (s == WAITING || s == TIMED_WAITING) {
> 631:             byte nonce;

Suggestion:

            byte seqNo;

src/java.base/share/classes/java/lang/VirtualThread.java line 948:

> 946:      * This method does nothing if the thread has been woken by notify or interrupt.
> 947:      */
> 948:     private void waitTimeoutExpired(byte nounce) {

I assume you meant `nonce` here, but please change to `seqNo`.

src/java.base/share/classes/java/lang/VirtualThread.java line 952:

> 950:         for (;;) {
> 951:             boolean unblocked = false;
> 952:             synchronized (timedWaitLock()) {

Where is the overall design of the timed-wait protocol and it use of synchronization described?

src/java.base/share/classes/java/lang/VirtualThread.java line 1397:

> 1395: 
> 1396:     /**
> 1397:      * Returns a lock object to coordinating timed-wait setup and timeout handling.

Suggestion:

     * Returns a lock object for coordinating timed-wait setup and timeout handling.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814158735
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814159210
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814169150
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814170953
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814171503
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1814172621


More information about the serviceability-dev mailing list