From 882a753883a5ba9a265db6de286a375b8045d621 Mon Sep 17 00:00:00 2001 From: Pieter Dirk Soels Date: Tue, 6 Feb 2024 10:33:22 +0100 Subject: [PATCH] Flag inner `Thread` methods for JDK 19 and greater (#395) Since JDK19, java.lang.Thread's methods have changed to support virtual threads. Specifically, for non-virtual threads, sleep(long) invokes sleep0(long) and yield() invokes yield0(). Moreover, since JDK21, sleep(long, int) and sleep(Duration) directly call sleep0(long) as opposed to sleep(long). Now, to identify all blocking java.lang.Thread calls, BlockHound will flag invocations of sleep0(long) and yield0() as opposed to sleep(long) and yield() when running on JDK19 or greater. Fixes #394. --- .../java/reactor/blockhound/BlockHound.java | 23 ++++++++++++++----- .../com/example/BlockingDisallowTest.java | 2 +- .../test/java/com/example/ReactorTest.java | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/agent/src/main/java/reactor/blockhound/BlockHound.java b/agent/src/main/java/reactor/blockhound/BlockHound.java index ec20b03..6c3ba5d 100644 --- a/agent/src/main/java/reactor/blockhound/BlockHound.java +++ b/agent/src/main/java/reactor/blockhound/BlockHound.java @@ -134,12 +134,6 @@ public TypePool typePool(ClassFileLocator classFileLocator, ClassLoader classLoa public static class Builder { private final Map>> blockingMethods = new HashMap>>() {{ - put("java/lang/Thread", new HashMap>() {{ - put("sleep", singleton("(J)V")); - put("yield", singleton("()V")); - put("onSpinWait", singleton("()V")); - }}); - put("java/lang/Object", new HashMap>() {{ put("wait", singleton("(J)V")); }}); @@ -219,6 +213,23 @@ public static class Builder { put("forkAndExec", singleton("(I[B[B[BI[BI[B[IZ)I")); }}); } + + try { + // Check if Java 19+ + Class.forName("java.lang.WrongThreadException"); + + put("java/lang/Thread", new HashMap>() {{ + put("sleep0", singleton("(J)V")); + put("yield0", singleton("()V")); + put("onSpinWait", singleton("()V")); + }}); + } catch (ClassNotFoundException __) { + put("java/lang/Thread", new HashMap>() {{ + put("sleep", singleton("(J)V")); + put("yield", singleton("()V")); + put("onSpinWait", singleton("()V")); + }}); + } }}; private final Map> allowances = new HashMap>() {{ diff --git a/example/src/test/java/com/example/BlockingDisallowTest.java b/example/src/test/java/com/example/BlockingDisallowTest.java index 1120a58..e5512a0 100644 --- a/example/src/test/java/com/example/BlockingDisallowTest.java +++ b/example/src/test/java/com/example/BlockingDisallowTest.java @@ -58,7 +58,7 @@ public void shouldDisallow() throws InterruptedException { //given the configuration we expect that Thread.yield() is allowed, but Thread.sleep() inside inner() isn't assertThat(boeRef.get()) .isNotNull() - .hasMessage("Blocking call! java.lang.Thread.sleep") + .hasMessageContaining("Blocking call! java.lang.Thread.sleep") .hasStackTraceContaining("at com.example.BlockingDisallowTest$NonBlockingClass.inner") .hasStackTraceContaining("at com.example.BlockingDisallowTest$NonBlockingClass.outer"); } diff --git a/example/src/test/java/com/example/ReactorTest.java b/example/src/test/java/com/example/ReactorTest.java index 7b4b77d..cd91bcf 100644 --- a/example/src/test/java/com/example/ReactorTest.java +++ b/example/src/test/java/com/example/ReactorTest.java @@ -124,14 +124,14 @@ public static Iterable data() { }); }); - tests.put("java.lang.Thread.sleep", () -> { + tests.put("java.lang.Thread." + (version.feature() >= 19 ? "sleep0" : "sleep"), () -> { return Mono.fromCallable(() -> { Thread.sleep(10); return ""; }); }); - tests.put("java.lang.Thread.yield", () -> { + tests.put("java.lang.Thread." + (version.feature() >= 19 ? "yield0" : "yield"), () -> { return Mono.fromCallable(() -> { Thread.yield(); return "";