square / okio

A modern I/O library for Android, Java, and Kotlin Multiplatform.
https://square.github.io/okio/
Apache License 2.0
8.72k stars 1.17k forks source link

RealBufferedSource.read() with length=0 blocks until there's some data, breaking InputStream.readNBytes #1476

Closed cgrushko closed 1 month ago

cgrushko commented 1 month ago

Repro:

Pipe p = new Pipe(1000);
Okio.buffer(p.sink()).write(new byte[10]).flush();

// This will block indefinitely:
Okio.buffer(p.source()).inputStream().readNBytes(10);

The code in JDK 21+35 LTS is:

while ((n = read(buf, nread,
     Math.min(buf.length - nread, remaining))) > 0) {
  nread += n;
  remaining -= n;
}

which effectively calls

read(buf, 0, 10) --> returns 10, do another iteration read(buf, 10, 0) --> gets stuck

IMO it's a reasonable implementation of readNBytes.

When we call read(0), RealBufferedSource.read() will call PipeSource.read(8192), which will block as it should. If RealBufferedSource.read() could return immediately when length=0, we won't get blocked.

swankjesse commented 1 month ago

Yeah, good advice. Wanna send a pull request? Otherwise I’m happy to fix it.

cgrushko commented 1 month ago

Yeah, good advice. Wanna send a pull request? Otherwise I’m happy to fix it.

It's a bit of a process to get the CLA signed for me, so I'd appreciate if you could take it. Also would like to take the opportunity to thank you folks for writing and maintaining okio and okhttp - it's an amazing piece of software :)