Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ as expected.

The [`ThreadNames`](nylon-threads/src/main/java/com/palantir/nylon/threads/VirtualThreads.java)
utility allows virtual threads to be safely used from libraries which target older jdk bytecode
when run using a jdk-21+ runtime.
when run using a jdk-24+ runtime. Using jdk24+ ensures that the changes from
[JEP-491 - Synchronize Virtual Threads without Pinning](https://openjdk.java.net/jeps/491) are available, preventing
potential deadlocks from virtual threads being pinned to platform threads.

VirtualThreads provides a static utility method to detect virtual threads:
```java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
public final class VirtualThreads {
private static final SafeLogger log = SafeLoggerFactory.get(VirtualThreads.class);

private static final Optional<VirtualThreadSupport> UNCHECKED_VIRTUAL_THREAD_SUPPORT = maybeInitializeUnchecked();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was slightly skeptical of using UNCHECKED_VIRTUAL_THREAD_SUPPORT in maybeInitialize but I think this is reasonable. Static variables are initialized in order of declaration in source code ("textual order"). From the language spec

The static initializers and class variable initializers are executed in textual order, and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope (§8.3.3).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be to make this a static field on ReflectiveVirtualThreadSupport instead, to avoid relying on field ordering, which is a bit scary?
(otherwise, please add a comment here explaining that these two fields' ordering must be kept the same)

private static final Optional<VirtualThreadSupport> VIRTUAL_THREAD_SUPPORT = maybeInitialize();

public static boolean isVirtual(Thread thread) {
Expand All @@ -48,22 +49,35 @@ public static Optional<VirtualThreadSupport> get() {
return VIRTUAL_THREAD_SUPPORT;
}

/**
* This method does not check if the runtime version is one that safely supports virtual threads (JDK24+). You
* should not use this method and should use {@link VirtualThreads#get()} instead.
*/
public static Optional<VirtualThreadSupport> dangerousGet() {
return UNCHECKED_VIRTUAL_THREAD_SUPPORT;
}

private static Optional<VirtualThreadSupport> maybeInitializeUnchecked() {
try {
return Optional.of(new ReflectiveVirtualThreadSupport());
} catch (Throwable t) {
log.warn("Virtual thread support is not available", t);
return Optional.empty();
}
}

private static Optional<VirtualThreadSupport> maybeInitialize() {
int featureVersion = Runtime.version().feature();
if (featureVersion < 21) {
// https://openjdk.org/jeps/491 - Synchronize Virtual Threads without Pinning was released in jdk24
if (featureVersion < 24) {
if (log.isDebugEnabled()) {
log.debug(
"Virtual threads are not available prior to jdk21",
"Virtual threads are not available prior to jdk24",
SafeArg.of("currentVersion", featureVersion));
}
return Optional.empty();
}
try {
return Optional.of(new ReflectiveVirtualThreadSupport());
} catch (Throwable t) {
log.warn("Virtual thread support is not available", t);
return Optional.empty();
}
return UNCHECKED_VIRTUAL_THREAD_SUPPORT;
}

private VirtualThreads() {}
Expand Down