diff --git a/src/main/scala/scalafix/internal/sbt/BlockingCache.scala b/src/main/scala/scalafix/internal/sbt/BlockingCache.scala index d1d4546e..4edba496 100644 --- a/src/main/scala/scalafix/internal/sbt/BlockingCache.scala +++ b/src/main/scala/scalafix/internal/sbt/BlockingCache.scala @@ -2,32 +2,26 @@ package scalafix.internal.sbt import java.{util => ju} -import scala.util.Try +import java.lang.ref.SoftReference -/** A basic thread-safe cache without any eviction. */ +/** A basic thread-safe cache with arbitrary eviction on GC pressure. */ class BlockingCache[K, V] { - private val underlying = new ju.concurrent.ConcurrentHashMap[K, V] - - private case class SkipUpdate(prev: V) extends Exception + private val underlying = + new ju.concurrent.ConcurrentHashMap[K, SoftReference[V]] // Evaluations of `transform` are guaranteed to be sequential for the same key - def compute(key: K, transform: Option[V] => Option[V]): V = { - Try( - underlying.compute( - key, - new ju.function.BiFunction[K, V, V] { - override def apply(key: K, prev: V): V = { - transform(Option(prev)).getOrElse(throw SkipUpdate(prev)) - } - } - ) - ).fold( - { - case SkipUpdate(prev) => prev - case ex => throw ex - }, - identity + def compute(key: K, transform: Option[V] => V): V = { + // keep the result in a strong reference to avoid eviction + // just after executing wrapped compute() + var result: V = null.asInstanceOf[V] + underlying.compute( + key, + (_, prev: SoftReference[V]) => { + result = transform(Option(prev).flatMap(ref => Option(ref.get))) + new SoftReference(result) + } ) + result } } diff --git a/src/main/scala/scalafix/internal/sbt/ScalafixInterface.scala b/src/main/scala/scalafix/internal/sbt/ScalafixInterface.scala index 57a27c8a..f2ed4605 100644 --- a/src/main/scala/scalafix/internal/sbt/ScalafixInterface.scala +++ b/src/main/scala/scalafix/internal/sbt/ScalafixInterface.scala @@ -154,9 +154,9 @@ object ScalafixInterface { None ), { - case Some(_) => + case Some(v) => // cache hit, don't update - None + v case None => // cache miss, resolve scalafix artifacts and classload them if (scalafixScalaMajorMinorVersion == "2.11") @@ -181,11 +181,9 @@ object ScalafixInterface { ) ) - Some( - ( - new ScalafixInterface(scalafixArguments).withArgs(printStream), - Nil - ) + ( + new ScalafixInterface(scalafixArguments).withArgs(printStream), + Nil ) } ) @@ -201,16 +199,14 @@ object ScalafixInterface { Some(toolClasspath) ), { - case Some((_, oldStamps)) if (currentStamps == oldStamps) => + case Some(v @ (_, oldStamps)) if (currentStamps == oldStamps) => // cache hit, don't update - None + v case _ => // cache miss or stale stamps, resolve custom rules artifacts and classload them - Some( - ( - buildinRulesInterface.withArgs(toolClasspath), - currentStamps - ) + ( + buildinRulesInterface.withArgs(toolClasspath), + currentStamps ) } )