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

Patricio Chilano Mateo pchilanomate at openjdk.org
Tue Oct 22 19:07:10 UTC 2024


On Tue, 22 Oct 2024 13:51:26 GMT, Axel Boldt-Christmas <aboldtch at openjdk.org> wrote:

>> 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
>
> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2234:
> 
>> 2232:     retry_fast_path = true;
>> 2233:   } else {
>> 2234:     relativize_chunk_concurrently(chunk);
> 
> Is the `relativize_chunk_concurrently` solution to the race only to have a single flag read in `can_thaw_fast` or is there some other subtlety here? 
> 
> While not required for the PR, if it is just to optimise the `can_thaw_fast` check, it can probably be made to work with one load and still allow concurrent gcs do fast_thaw when we only get here due to a lockstack.

Yes, it's just to do a single read. I guess you are thinking of combining flags and lockStackSize into a int16_t?

> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2247:
> 
>> 2245:     _thread->lock_stack().move_from_address(tmp_lockstack, lockStackSize);
>> 2246: 
>> 2247:     chunk->set_lockstack_size(0);
> 
> After some discussion here at the office we think there might be an issue here with simply hiding the oops without clearing them. Below in `recurse_thaw` we `do_barriers`. But it does not touch these lockstack. Missing the SATB store barrier is probably fine from a liveness perspective, because the oops in the lockstack must also be in the frames. But removing the oops without a barrier and clear will probably lead to problems down the line.
> 
> Something like the following would probably handle this. Or even fuse the `copy_lockstack` and `clear_lockstack` together into some kind of `transfer_lockstack` which both loads and clears the oops.
> 
> 
> diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp
> index d3d63533eed..f737bd2db71 100644
> --- a/src/hotspot/share/oops/stackChunkOop.cpp
> +++ b/src/hotspot/share/oops/stackChunkOop.cpp
> @@ -470,6 +470,28 @@ void stackChunkOopDesc::copy_lockstack(oop* dst) {
>    }
>  }
>  
> +void stackChunkOopDesc::clear_lockstack() {
> +  const int cnt = lockstack_size();
> +  const bool requires_gc_barriers = is_gc_mode() || requires_barriers();
> +  const bool requires_uncompress = has_bitmap() && UseCompressedOops;
> +  const auto clear_obj = [&](intptr_t* at) {
> +    if (requires_uncompress) {
> +      HeapAccess<>::oop_store(reinterpret_cast<narrowOop*>(at), nullptr);
> +    } else {
> +      HeapAccess<>::oop_store(reinterpret_cast<oop*>(at), nullptr);
> +    }
> +  };
> +
> +  if (requires_gc_barriers) {
> +    intptr_t* lockstack_start = start_address();
> +    for (int i = 0; i < cnt; i++) {
> +      clear_obj(&lockstack_start[i]);
> +    }
> +  }
> +  set_lockstack_size(0);
> +  set_has_lockstack(false);
> +}
> +
>  void stackChunkOopDesc::print_on(bool verbose, outputStream* st) const {
>    if (*((juint*)this) == badHeapWordVal) {
>      st->print_cr("BAD WORD");
> diff --git a/src/hotspot/share/oops/stackChunkOop.hpp b/src/hotspot/share/oops/stackChunkOop.hpp
> index 28e0576801e..928e94dd695 100644
> --- a/src/hotspot/share/oops/stackChunkOop.hpp
> +++ b/src/hotspot/share/oops/stackChunkOop.hpp
> @@ -167,6 +167,7 @@ class stackChunkOopDesc : public instanceOopDesc {
>    void fix_thawed_frame(const frame& f, const RegisterMapT* map);
>  
>    void copy_lockstack(oop* start);
> +  void clear_lockstack();
>  
>    template <typename OopT, class StackChunkLockSta...

Ok, I'll change copy_lockstack to both load and clear the oops in the same method. Now, when we call do_barriers on recurse_thaw we don't clear the oops, we just load and store the loaded value again. Is it the case that we just need to do a store, so that already works, or are we missing clearing the oops from the copied frames?

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

PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1811244844
PR Review Comment: https://git.openjdk.org/jdk/pull/21565#discussion_r1811244206


More information about the serviceability-dev mailing list