On Tue, 6 Aug 2024 17:26:55 GMT, Jorn Vernee <[email protected]> wrote:
> As discussed in the JBS issue:
>
> FFM upcall stubs embed a `Method*` of the target method in the stub. This
> `Method*` is read from the `LambdaForm::vmentry` field associated with the
> target method handle at the time when the upcall stub is generated. The MH
> instance itself is stashed in a global JNI ref. So, there should be a
> reachability chain to the holder class: `MH (receiver) -> LF (form) ->
> MemberName (vmentry) -> ResolvedMethodName (method) -> Class<?> (vmholder)`
>
> However, it appears that, due to multiple threads racing to initialize the
> `vmentry` field of the `LambdaForm` of the target method handle of an upcall
> stub, it is possible that the `vmentry` is updated _after_ we embed the
> corresponding `Method`* into an upcall stub (or rather, the latest update is
> not visible to the thread generating the upcall stub). Technically, it is
> fine to keep using a 'stale' `vmentry`, but the problem is that now the
> reachability chain is broken, since the upcall stub only extracts the target
> `Method*`, and doesn't keep the stale `vmentry` reachable. The holder class
> can then be unloaded, resulting in a crash.
>
> The fix I've chosen for this is to mimic what we already do in
> `MethodHandles::jump_to_lambda_form`, and re-load the `vmentry` field from
> the target method handle each time. Luckily, this does not really seem to
> impact performance.
>
> <details>
> <summary>Performance numbers</summary>
> x64:
>
> before:
>
> Benchmark Mode Cnt Score Error Units
> Upcalls.panama_blank avgt 30 69.216 ± 1.791 ns/op
>
>
> after:
>
> Benchmark Mode Cnt Score Error Units
> Upcalls.panama_blank avgt 30 67.787 ± 0.684 ns/op
>
>
> aarch64:
>
> before:
>
> Benchmark Mode Cnt Score Error Units
> Upcalls.panama_blank avgt 30 61.574 ± 0.801 ns/op
>
>
> after:
>
> Benchmark Mode Cnt Score Error Units
> Upcalls.panama_blank avgt 30 61.218 ± 0.554 ns/op
>
> </details>
>
> As for the added TestUpcallStress test, it takes about 800 seconds to run
> this test on the dev machine I'm using, so I've set the timeout quite high.
> Since it runs for so long, I've dropped it from the default `jdk_foreign`
> test suite, which runs in tier2. Instead the new test will run in tier4.
>
> Testing: tier 1-4
src/hotspot/cpu/riscv/upcallLinker_riscv.cpp line 264:
> 262:
> 263: __ block_comment("{ load target ");
> 264: __ movptr(j_rarg0, (intptr_t) receiver);
Hi @JornVernee , Could you please apply following small add-on change for
linux-riscv64? As I witnessed build warning with GCC-13. Otherwise, builds fine
and the newly-added test/jdk/java/foreign/TestUpcallStress.java is passing.
diff --git a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp
b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp
index 5c45a679316..55160be99d0 100644
--- a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp
+++ b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp
@@ -261,7 +261,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver,
Symbol* signature,
__ block_comment("} argument shuffle");
__ block_comment("{ load target ");
- __ movptr(j_rarg0, (intptr_t) receiver);
+ __ movptr(j_rarg0, (address) receiver);
__ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); //
loads Method* into xmethod
__ block_comment("} load target ");
diff --git a/test/jdk/java/foreign/TestUpcallStress.java
b/test/jdk/java/foreign/TestUpcallStress.java
index 3b9b1d4b207..40607746856 100644
--- a/test/jdk/java/foreign/TestUpcallStress.java
+++ b/test/jdk/java/foreign/TestUpcallStress.java
@@ -24,7 +24,7 @@
/*
* @test
* @requires jdk.foreign.linker != "FALLBACK"
- * @requires os.arch == "aarch64" & os.name == "Linux"
+ * @requires (os.arch == "aarch64" | os.arch=="riscv64") & os.name == "Linux"
* @requires os.maxMemory > 4G
* @modules java.base/jdk.internal.foreign
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/20479#discussion_r1743130094