bloop icon indicating copy to clipboard operation
bloop copied to clipboard

Favor NIO over IO to avoid FileSystemExceptions on Windows

Open alexarchambault opened this issue 10 months ago • 1 comments

On Windows, when Bloop compiled a project using a locally published JAR, it seems it still holds an exclusive access to the file. This is a problem when one wants to overwrite the locally published artifact from a build tool. One gets errors like

native.publishLocal java.nio.file.FileSystemException: C:\Users\Alex\.ivy2\local\io.github.alexarchambault.native-terminal\native-terminal\0.0.7-SNAPSHOT\jars\native-terminal.jar: The process cannot access the file because it is being used by another process
    java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
    java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
    java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
    java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:273)
    java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:104)
    java.base/java.nio.file.Files.delete(Files.java:1152)
    os.remove$all$.apply(FileOps.scala:341)
    os.copy$over$.apply(FileOps.scala:300)
    mill.scalalib.publish.LocalIvyPublisher.$anonfun$publishLocal$2(LocalIvyPublisher.scala:46)
    scala.collection.immutable.List.map(List.scala:247)
    scala.collection.immutable.List.map(List.scala:79)
    mill.scalalib.publish.LocalIvyPublisher.publishLocal(LocalIvyPublisher.scala:44)
    mill.scalalib.PublishModule.$anonfun$publishLocalTask$2(PublishModule.scala:231)

Restarting the Bloop server solves that problem, but isn't very handy.

Favoring Java NIO over Java IO seems to solve that problem, like done here or here.

alexarchambault avatar Jan 10 '25 10:01 alexarchambault

This might help with semanticdb on Windows as well. Bloop clashing with Metals....

java.nio.file.FileSystemException: [XXX]\bloop-bsp-clients-classes\classes-Metals-GqF3NKlrSiGqKS_brmBrFg==\META-INF\semanticdb\src\main\scala\Foo.scala.semanticdb: The process cannot access the file because it is being used by another process
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
	at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:234)
	at java.nio.file.Files.newByteChannel(Files.java:379)
	at java.nio.file.Files.newByteChannel(Files.java:431)
	at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
	at java.nio.file.Files.newInputStream(Files.java:159)
	at scala.meta.internal.mtags.Semanticdbs$.loadTextDocuments(Semanticdbs.scala:24)
	at scala.meta.internal.mtags.Semanticdbs$.loadResolvedTextDocument(Semanticdbs.scala:71)
	at scala.meta.internal.mtags.Semanticdbs$.loadTextDocument(Semanticdbs.scala:55)
	at scala.meta.internal.metals.FileSystemSemanticdbs.textDocument(FileSystemSemanticdbs.scala:67)
	at scala.meta.internal.metals.AggregateSemanticdbs.loop$1(AggregateSemanticdbs.scala:30)
	at scala.meta.internal.metals.AggregateSemanticdbs.textDocument(AggregateSemanticdbs.scala:36)
	at scala.meta.internal.metals.CodeLensProvider.findLenses(CodeLensProvider.scala:25)
	at scala.meta.internal.metals.MetalsLspService.$anonfun$codeLens$3(MetalsLspService.scala:1194)
	at scala.meta.internal.metals.TimerProvider.timedThunk(TimerProvider.scala:25)
	at scala.meta.internal.metals.MetalsLspService.$anonfun$codeLens$2(MetalsLspService.scala:1192)
	at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:470)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.lang.Thread.run(Thread.java:1583)

Although the file might be unreadable anyway if it's only half written at this point

Arthurm1 avatar Mar 18 '25 16:03 Arthurm1

Hi! I’d like to pick up this issue.

From what I understand, the root cause is that Bloop is holding open file handles on Windows when reading JARs or semanticdb files via classic java.io APIs, preventing tools like Mill, Ivy, or Metals from overwriting or deleting those files.

Switching to NIO (Files.newInputStream, Files.newByteChannel, or using Path-based APIs with proper try-with-resources) should avoid those lingering handles.

My plan:

  • Reproduce the lock behavior on Windows using a locally published JAR
  • Identify where IO-based access is happening in Bloop’s classpath + jar handling
  • Patch those locations to use NIO with explicit closing
  • Verify that Mill/metals steps can overwrite/delete the JAR afterwards

If this sounds correct, could you please assign me @tgodzik ? I’m happy to work on a PR.

arnavsharma990 avatar Nov 27 '25 15:11 arnavsharma990