Skip to content

Commit ca0420f

Browse files
He-Pinlrytz
authored andcommitted
Improve Await.result/ready performance for completed futures
Avoid evaluating `value0` twice on completed Futures for performance.
1 parent 76fa46c commit ca0420f

File tree

3 files changed

+30
-10
lines changed

3 files changed

+30
-10
lines changed

library/src/scala/concurrent/Future.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,12 @@ object Future {
574574
private[this] final val _addToBuilderFun: (Builder[Any, Nothing], Any) => Builder[Any, Nothing] = (b: Builder[Any, Nothing], e: Any) => b += e
575575
private[concurrent] final def addToBuilderFun[A, M] = _addToBuilderFun.asInstanceOf[Function2[Builder[A, M], A, Builder[A, M]]]
576576

577+
private[concurrent] def waitUndefinedError(): Nothing =
578+
throw new IllegalArgumentException("Cannot wait for Undefined duration of time")
579+
580+
private[concurrent] def timeoutError(delay: Duration): Nothing =
581+
throw new TimeoutException(s"Future timed out after [$delay]")
582+
577583
/** A Future which is never completed.
578584
*/
579585
object never extends Future[Nothing] {
@@ -583,7 +589,7 @@ object Future {
583589
override final def ready(atMost: Duration)(implicit permit: CanAwait): this.type = {
584590
import Duration.{Undefined, Inf, MinusInf}
585591
atMost match {
586-
case u if u eq Undefined => throw new IllegalArgumentException("cannot wait for Undefined period")
592+
case u if u eq Undefined => waitUndefinedError()
587593
case `Inf` =>
588594
while(!Thread.interrupted()) {
589595
LockSupport.park(this)
@@ -603,14 +609,14 @@ object Future {
603609
case _: FiniteDuration => // Drop out if 0 or less
604610
case x: Duration.Infinite => throw new MatchError(x)
605611
}
606-
throw new TimeoutException(s"Future timed out after [$atMost]")
612+
timeoutError(atMost)
607613
}
608614

609615
@throws[TimeoutException]
610616
@throws[InterruptedException]
611617
override final def result(atMost: Duration)(implicit permit: CanAwait): Nothing = {
612618
ready(atMost)
613-
throw new TimeoutException(s"Future timed out after [$atMost]")
619+
timeoutError(atMost)
614620
}
615621

616622
override final def onComplete[U](f: Try[Nothing] => U)(implicit executor: ExecutionContext): Unit = ()

library/src/scala/concurrent/impl/Promise.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,9 @@ private[concurrent] object Promise {
255255
l.result
256256
}
257257
if (r ne null) r
258-
else throw new TimeoutException("Future timed out after [" + atMost + "]")
258+
else Future.timeoutError(atMost)
259259
}
260-
} else throw new IllegalArgumentException("Cannot wait for Undefined duration of time")
260+
} else Future.waitUndefinedError()
261261

262262
@throws(classOf[TimeoutException])
263263
@throws(classOf[InterruptedException])

library/src/scala/concurrent/package.scala

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212

1313
package scala
1414

15-
import scala.concurrent.duration.Duration
1615
import scala.annotation.implicitNotFound
16+
import scala.concurrent.duration.Duration
17+
import scala.util.Try
1718

1819
/** This package object contains primitives for concurrent and parallel programming.
1920
*
@@ -170,8 +171,11 @@ package concurrent {
170171
@throws(classOf[TimeoutException])
171172
@throws(classOf[InterruptedException])
172173
final def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = awaitable match {
173-
case f: Future[T] if f.isCompleted => awaitable.ready(atMost)(AwaitPermission)
174-
case _ => blocking(awaitable.ready(atMost)(AwaitPermission))
174+
case f: Future[T] if f.isCompleted =>
175+
if (atMost eq Duration.Undefined) Future.waitUndefinedError() // preserve semantics, see scala/scala#10972
176+
else awaitable
177+
case _ =>
178+
blocking(awaitable.ready(atMost)(AwaitPermission))
175179
}
176180

177181
/**
@@ -197,8 +201,18 @@ package concurrent {
197201
@throws(classOf[TimeoutException])
198202
@throws(classOf[InterruptedException])
199203
final def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable match {
200-
case f: Future[T] if f.isCompleted => f.result(atMost)(AwaitPermission)
201-
case _ => blocking(awaitable.result(atMost)(AwaitPermission))
204+
case FutureValue(v) =>
205+
if (atMost eq Duration.Undefined) Future.waitUndefinedError() // preserve semantics, see scala/scala#10972
206+
else v.get
207+
case _ =>
208+
blocking(awaitable.result(atMost)(AwaitPermission))
209+
}
210+
211+
private object FutureValue {
212+
def unapply[T](a: Awaitable[T]): Option[Try[T]] = a match {
213+
case f: Future[T] => f.value
214+
case _ => None
215+
}
202216
}
203217
}
204218
}

0 commit comments

Comments
 (0)