Skip to content

Instantly share code, notes, and snippets.

@swankjesse
Created February 4, 2025 14:45
Show Gist options
  • Save swankjesse/d995ba7c79b27193fa7a908c7334ba46 to your computer and use it in GitHub Desktop.
Save swankjesse/d995ba7c79b27193fa7a908c7334ba46 to your computer and use it in GitHub Desktop.
@Test
fun closeAJarFileWhileSomebodyIsReadingIt() {
val someJarResource = this::class.java.classLoader.getResource("LICENSE-junit.txt")!!
val connectionA = someJarResource.openConnection()
val sourceA = connectionA.getInputStream().source().buffer()
val connectionB = someJarResource.openConnection()
val sourceB = connectionB.getInputStream().source().buffer()
connectionA.useCaches = false
assertThat(sourceA.readUtf8()).contains("JUnit")
sourceA.close()
assertThat(sourceB.readUtf8()).contains("JUnit")
sourceB.close()
}
@swankjesse
Copy link
Author

This crashes on line 15:

Stream closed
java.io.IOException: Stream closed
	at java.base/java.util.zip.InflaterInputStream.ensureOpen(InflaterInputStream.java:68)
	at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:143)
	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:119)
	at okio.InputStreamSource.read(JvmOkio.kt:93)
	at okio.Buffer.writeAll(Buffer.kt:1309)
	at okio.RealBufferedSource.readUtf8(RealBufferedSource.kt:325)
	at closeAJarFileWhileSomebodyIsReadingIt(FileLeakTest.kt:172)

@swankjesse
Copy link
Author

It doesn’t crash if you remove this line:

connectionA.useCaches = false

And it doesn’t crash if you don’t open sourceB until after closing sourceA.

@swankjesse
Copy link
Author

Setting useCaches to false causes sourceA to close a .jar file that it doesn’t own.

@jclyne
Copy link

jclyne commented Feb 4, 2025

awesome! explains the race condition

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment