Skip to content

Commit

Permalink
Flag inner Thread methods for JDK 19 and greater (#395)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Badbond committed Feb 6, 2024
1 parent 5581929 commit 882a753
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 9 deletions.
23 changes: 17 additions & 6 deletions agent/src/main/java/reactor/blockhound/BlockHound.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,6 @@ public TypePool typePool(ClassFileLocator classFileLocator, ClassLoader classLoa
public static class Builder {

private final Map<String, Map<String, Set<String>>> blockingMethods = new HashMap<String, Map<String, Set<String>>>() {{
put("java/lang/Thread", new HashMap<String, Set<String>>() {{
put("sleep", singleton("(J)V"));
put("yield", singleton("()V"));
put("onSpinWait", singleton("()V"));
}});

put("java/lang/Object", new HashMap<String, Set<String>>() {{
put("wait", singleton("(J)V"));
}});
Expand Down Expand Up @@ -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<String, Set<String>>() {{
put("sleep0", singleton("(J)V"));
put("yield0", singleton("()V"));
put("onSpinWait", singleton("()V"));
}});
} catch (ClassNotFoundException __) {
put("java/lang/Thread", new HashMap<String, Set<String>>() {{
put("sleep", singleton("(J)V"));
put("yield", singleton("()V"));
put("onSpinWait", singleton("()V"));
}});
}
}};

private final Map<String, Map<String, Boolean>> allowances = new HashMap<String, Map<String, Boolean>>() {{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
4 changes: 2 additions & 2 deletions example/src/test/java/com/example/ReactorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ public static Iterable<Object[]> 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 "";
Expand Down

0 comments on commit 882a753

Please sign in to comment.