On Tue, 22 Oct 2024 02:14:23 GMT, Patricio Chilano Mateo 
<pchilanom...@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 six 
> additional commits since the last revision:
> 
>  - Fix comments in objectMonitor.hpp
>  - Move frame::saved_thread_address() to platform dependent files
>  - Fix typo in jvmtiExport.cpp
>  - remove usage of frame::metadata_words in possibly_adjust_frame()
>  - Fix comments in c2 locking paths
>  - Revert and simplify changes to c1_Runtime1 on aarch64 and riscv

First, congratulations on an exceptional piece of work @pchilano .  

Also thank you for the very clear breakdown and description in the PR as that 
helps immensely with trying to digest a change of this size.

The overall operational behaviour of this change seems very solid. My only 
concern is whether the unparker thread may become a bottleneck in some 
scenarios, but that is a bridge we will have to cross if we come to it.

My initial comments mainly come from just trying to understand the top-level 
changes around the use of the thread-id as the monitor owner. I have a number 
of suggestions on naming (mainly `is_x` versus `has_x`) and on documenting the 
API methods more clearly. None of which are showstoppers and some of which 
pre-exist. Unfortunately though you will need to fix the spelling of `succesor`.

Thanks

src/hotspot/share/runtime/objectMonitor.hpp line 47:

> 45: // ParkEvent instead.  Beware, however, that the JVMTI code
> 46: // knows about ObjectWaiters, so we'll have to reconcile that code.
> 47: // See next_waiter(), first_waiter(), etc.

This to-do is likely no longer relevant with the current changes.

src/hotspot/share/runtime/objectMonitor.hpp line 288:

> 286:   // Returns true if this OM has an owner, false otherwise.
> 287:   bool      has_owner() const;
> 288:   int64_t   owner() const;  // Returns null if DEFLATER_MARKER is 
> observed.

null is not an int64_t value.

src/hotspot/share/runtime/objectMonitor.hpp line 292:

> 290: 
> 291:   static int64_t owner_for(JavaThread* thread);
> 292:   static int64_t owner_for_oop(oop vthread);

Some comments describing this API would be good. I'm struggling a bit with the 
"owner for" terminology. I think `owner_from` would be better. And can't these 
just overload rather than using different names?

src/hotspot/share/runtime/objectMonitor.hpp line 302:

> 300:   // Simply set _owner field to new_value; current value must match 
> old_value.
> 301:   void      set_owner_from_raw(int64_t old_value, int64_t new_value);
> 302:   void      set_owner_from(int64_t old_value, JavaThread* current);

Again some comments describing API would good. The old API had vague names like 
old_value and new_value because of the different forms the owner value could 
take. Now it is always a thread-id we can do better I think. The distinction 
between the raw and non-raw forms is unclear and the latter is not covered by 
the initial comment.

src/hotspot/share/runtime/objectMonitor.hpp line 303:

> 301:   void      set_owner_from_raw(int64_t old_value, int64_t new_value);
> 302:   void      set_owner_from(int64_t old_value, JavaThread* current);
> 303:   // Simply set _owner field to current; current value must match 
> basic_lock_p.

Comment is no longer accurate

src/hotspot/share/runtime/objectMonitor.hpp line 309:

> 307:   // _owner field. Returns the prior value of the _owner field.
> 308:   int64_t   try_set_owner_from_raw(int64_t old_value, int64_t new_value);
> 309:   int64_t   try_set_owner_from(int64_t old_value, JavaThread* current);

Similar to set_owner* need better comments describing API.

src/hotspot/share/runtime/objectMonitor.hpp line 311:

> 309:   int64_t   try_set_owner_from(int64_t old_value, JavaThread* current);
> 310: 
> 311:   bool      is_succesor(JavaThread* thread);

I think `has_successor` is more appropriate here as it is not the monitor that 
is the successor.

src/hotspot/share/runtime/objectMonitor.hpp line 315:

> 313:   void      set_succesor(oop vthread);
> 314:   void      clear_succesor();
> 315:   bool      has_succesor();

Sorry but `successor` has two `s` before `or`.

src/hotspot/share/runtime/objectMonitor.hpp line 317:

> 315:   bool      has_succesor();
> 316: 
> 317:   bool is_owner(JavaThread* thread) const { return owner() == 
> owner_for(thread); }

Again `has_owner` seems more appropriate

src/hotspot/share/runtime/objectMonitor.hpp line 323:

> 321:   }
> 322: 
> 323:   bool is_owner_anonymous() const { return owner_raw() == 
> ANONYMOUS_OWNER; }

Again I struggle with the pre-existing `is_owner` formulation here. The target 
of the expression is a monitor and we are asking if the monitor has an 
anonymous owner.

src/hotspot/share/runtime/objectMonitor.hpp line 333:

> 331:   bool is_stack_locker(JavaThread* current);
> 332:   BasicLock* stack_locker() const;
> 333:   void set_stack_locker(BasicLock* locker);

Again `is` versus `has`, plus some general comments describing the API.

src/hotspot/share/runtime/threadIdentifier.cpp line 30:

> 28: 
> 29: // starting at 3, excluding reserved values defined in ObjectMonitor.hpp
> 30: static const int64_t INITIAL_TID = 3;

Can we express this in terms of those reserved values, or are they inaccessible?

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

> 729: 
> 730:         if (attached && VM.initLevel() < 1) {
> 731:             this.tid = 3;  // primordial thread

The comment before the `ThreadIdentifiers` class needs updating to account for 
this change.

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

> 107:      *
> 108:      *   RUNNING -> BLOCKING       // blocking on monitor enter
> 109:      *  BLOCKING -> BLOCKED        // blocked on monitor enter

Should this say something similar to the parked case, about the "yield" being 
successful?

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

> 108:      *   RUNNING -> BLOCKING       // blocking on monitor enter
> 109:      *  BLOCKING -> BLOCKED        // blocked on monitor enter
> 110:      *   BLOCKED -> UNBLOCKED      // unblocked, may be scheduled to 
> continue

Does this mean it now owns the monitor, or just it is able to re-contest for 
monitor entry?

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

> 109:      *  BLOCKING -> BLOCKED        // blocked on monitor enter
> 110:      *   BLOCKED -> UNBLOCKED      // unblocked, may be scheduled to 
> continue
> 111:      * UNBLOCKED -> RUNNING        // continue execution after blocked 
> on monitor enter

Presumably this one means it acquired the monitor?

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

> 113:      *   RUNNING -> WAITING        // transitional state during wait on 
> monitor
> 114:      *   WAITING -> WAITED         // waiting on monitor
> 115:      *    WAITED -> BLOCKED        // notified, waiting to be unblocked 
> by monitor owner

Waiting to re-enter the monitor?

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

> 176:     // timed-wait support
> 177:     private long waitTimeout;
> 178:     private byte timedWaitNonce;

Strange name - what does this mean?

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

> 528:                 && carrier == Thread.currentCarrierThread();
> 529:         carrier.setCurrentThread(carrier);
> 530:         Thread.setCurrentLockId(this.threadId()); // keep lock ID of 
> virtual thread

I'm struggling to understand the different threads in play when this is called 
and what the method actual does to which threads. ??

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

Changes requested by dholmes (Reviewer).

PR Review: https://git.openjdk.org/jdk/pull/21565#pullrequestreview-2384039238
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810025380
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810027786
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810029858
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810032387
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810033016
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810035434
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810037658
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810036007
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810041017
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810046285
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810049295
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810068395
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810076019
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810111255
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810113028
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810113953
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810114488
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810116177
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1810131339

Reply via email to