diff --git a/io/src/main/scala/sbt/internal/io/Milli.scala b/io/src/main/scala/sbt/internal/io/Milli.scala index e5962dd8..4d8f1dc4 100644 --- a/io/src/main/scala/sbt/internal/io/Milli.scala +++ b/io/src/main/scala/sbt/internal/io/Milli.scala @@ -79,6 +79,7 @@ private abstract class MilliNative[Native] extends Milli { * FFI for standard C library. */ private sealed trait PosixBase extends Library { + /** http://www.cplusplus.com/reference/cstring/strerror/ */ def strerror(errnum: Int): String } @@ -368,7 +369,7 @@ object Milli { } } - private val milli = + val milli: Milli = if (jdkTimestamps || !isIntel) JavaMilli else diff --git a/io/src/main/scala/sbt/internal/io/Retry.scala b/io/src/main/scala/sbt/internal/io/Retry.scala index 636f6998..94ece31f 100644 --- a/io/src/main/scala/sbt/internal/io/Retry.scala +++ b/io/src/main/scala/sbt/internal/io/Retry.scala @@ -11,7 +11,6 @@ package sbt.internal.io import java.io.IOException -import scala.annotation.tailrec import scala.util.control.NonFatal private[sbt] object Retry { @@ -20,35 +19,29 @@ private[sbt] object Retry { try System.getProperty("sbt.io.retry.limit", defaultLimit.toString).toInt catch { case NonFatal(_) => defaultLimit } } - private[sbt] def apply[T](f: => T, excludedExceptions: Class[_ <: IOException]*): T = + private[sbt] def apply[@specialized T](f: => T, excludedExceptions: Class[_ <: IOException]*): T = apply(f, limit, excludedExceptions: _*) - private[sbt] def apply[T]( + private[sbt] def apply[@specialized T]( f: => T, limit: Int, excludedExceptions: Class[_ <: IOException]* ): T = { - lazy val filter: Exception => Boolean = excludedExceptions match { + def filter(e: Exception): Boolean = excludedExceptions match { case s if s.nonEmpty => - (e: Exception) => !excludedExceptions.exists(_.isAssignableFrom(e.getClass)) + !excludedExceptions.exists(_.isAssignableFrom(e.getClass)) case _ => - (_: Exception) => true + true } - @tailrec - def impl(attempt: Int): T = { - val (retry, res) = try false -> Right(f) - catch { - case e: IOException if filter(e) && (attempt < limit) => (true, Left(e)) - case e: IOException => (false, Left(e)) - } - if (retry) { - Thread.sleep(0); impl(attempt + 1) - } else { - res match { - case Right(r) => r - case Left(e) => throw e - } + var attempt = 1 + while (attempt < limit) { + try { + return f + } catch { + case e: IOException if filter(e) && (attempt < limit) => + Thread.sleep(0); + attempt += 1 } } - impl(1) + throw new IllegalStateException() } } diff --git a/io/src/main/scala/sbt/internal/nio/SwovalConverters.scala b/io/src/main/scala/sbt/internal/nio/SwovalConverters.scala index b1d40f8c..81d080be 100644 --- a/io/src/main/scala/sbt/internal/nio/SwovalConverters.scala +++ b/io/src/main/scala/sbt/internal/nio/SwovalConverters.scala @@ -54,7 +54,9 @@ private[sbt] object SwovalFileTreeView extends FileTreeView.Nio[FileAttributes] } result.result() }, - classOf[NotDirectoryException], - classOf[NoSuchFileException] + excludedExceptions: _* ) + + private val excludedExceptions = + List(classOf[NotDirectoryException], classOf[NoSuchFileException]) } diff --git a/io/src/main/scala/sbt/io/IO.scala b/io/src/main/scala/sbt/io/IO.scala index 3d95d299..78bb2948 100644 --- a/io/src/main/scala/sbt/io/IO.scala +++ b/io/src/main/scala/sbt/io/IO.scala @@ -27,6 +27,7 @@ import sbt.nio.file.FileTreeView import scala.Function.tupled import scala.annotation.tailrec import scala.collection.JavaConverters._ +import scala.collection.immutable import scala.collection.immutable.TreeSet import scala.collection.mutable.{ HashMap, HashSet } import scala.reflect.{ Manifest => SManifest } @@ -1404,7 +1405,7 @@ object IO { */ def getModifiedTimeOrZero(file: File): Long = try { - Retry(Milli.getModifiedTime(file), classOf[FileNotFoundException]) + Retry(Milli.getModifiedTime(file), excludeFileNotFound: _*) } catch { case _: FileNotFoundException => val unnormalized = file.toPath @@ -1429,7 +1430,7 @@ object IO { */ def setModifiedTimeOrFalse(file: File, mtime: Long): Boolean = try { - Retry(Milli.setModifiedTime(file, mtime), classOf[FileNotFoundException]) + Retry(Milli.setModifiedTime(file, mtime), excludeFileNotFound: _*) true } catch { case _: FileNotFoundException => @@ -1461,4 +1462,8 @@ object IO { // see Java bug #6791812 setModifiedTimeOrFalse(targetFile, math.max(last, 0L)) } + + private val excludeFileNotFound: immutable.Seq[Class[_ <: IOException]] = List( + classOf[FileNotFoundException] + ) } diff --git a/io/src/main/scala/sbt/nio/file/FileTreeView.scala b/io/src/main/scala/sbt/nio/file/FileTreeView.scala index e9e5a670..9029d9e8 100644 --- a/io/src/main/scala/sbt/nio/file/FileTreeView.scala +++ b/io/src/main/scala/sbt/nio/file/FileTreeView.scala @@ -53,16 +53,18 @@ object FileTreeView { * An implementation of [[FileTreeView]] that uses built in jvm apis. This implementation * will throw an IOException if the input path is not a directory or doesn't exist. */ - val nio: FileTreeView.Nio[FileAttributes] = (path: Path) => - Retry( - { - val stream = Files.list(path) - try stream.iterator.asScala.flatMap(p => FileAttributes(p).toOption.map(p -> _)).toVector - finally stream.close() - }, - classOf[NotDirectoryException], - classOf[NoSuchFileException] - ) + val nio: FileTreeView.Nio[FileAttributes] = { + val excludedExceptions = List(classOf[NotDirectoryException], classOf[NoSuchFileException]) + (path: Path) => + Retry( + { + val stream = Files.list(path) + try stream.iterator.asScala.flatMap(p => FileAttributes(p).toOption.map(p -> _)).toVector + finally stream.close() + }, + excludedExceptions: _* + ) + } /** * Adds additional methods to [[FileTreeView]]. This api may be changed so it should not be