Skip to content

ConstrainableInputStream pins virtual threads (Java-21) #2054

@malkusch

Description

@malkusch

Sample to reproduce:

InputStream in = load();
Jsoup.parse(in, "UTF-8", "http://example.org/")

Running that with -Djdk.tracePinnedThreads=full in Java-21(on a virtual thread) will give the following print:

Thread[#35,ForkJoinPool-1-worker-4,5,CarrierThreads]
  …
  java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:405) <== monitors:1
  org.jsoup.internal.ConstrainableInputStream.read(ConstrainableInputStream.java:64)
  …

BufferedInputStream was refactored to be virtual thread friendly, however with a caveat: It becomes unfriendly when inherited (which ConstrainableInputStream does):

    public BufferedInputStream(InputStream in, int size) {
        …
        if (getClass() == BufferedInputStream.class) { // false in case of ConstrainableInputStream
            lock = InternalLock.newLockOrNull();
            …
        } else {
            lock = null;
            …
        }
    }
…
    public int read(byte[] b, int off, int len) throws IOException {
        if (lock != null) {
            … // Virtual Thread friendly code
        } else {
            synchronized (this) {
                return implRead(b, off, len); // Line 405
            }
        }
    }

With Loom gaining popularity with the latest LTS release, do you want to reconsider ConstrainableInputStream inheriting from BufferedInputStream? A possible mitigation might be to use composition instead, i.e. inherit from FilterInputStream and use a new instance of BufferedInputStream as source:

class ConstrainableInputStream extends FilterInputStream {
…
  static ConstrainableInputStream wrap(InputStream in,…) {
    var buffered = new BufferedInputStream(in);
    return new ConstrainableInputStream(buffered);
  }
}

Metadata

Metadata

Assignees

Labels

improvementAn improvement / new feature idea

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions