diff options
author | Roland <rk@rkuhn.info> | 2012-09-11 17:58:35 +0200 |
---|---|---|
committer | Roland <rk@rkuhn.info> | 2012-09-11 17:58:35 +0200 |
commit | ad6c261eafbe72e014d005d0452c8a628a07f123 (patch) | |
tree | 9d96d5c18f44ea1bb6305d8b689019864f90a3b4 | |
parent | 3767a6a83efc74d34e9025f798eeb2a043e6df8d (diff) | |
download | scala-ad6c261eafbe72e014d005d0452c8a628a07f123.tar.gz scala-ad6c261eafbe72e014d005d0452c8a628a07f123.tar.bz2 scala-ad6c261eafbe72e014d005d0452c8a628a07f123.zip |
improve docs and Promise impl
- scaladoc the exceptions thrown by Await.* and Awaitable.*
- move intercept[Exception] into partest’s TestUtil object
- improve Promise.tryAwait implementation following Viktor’s comments
and make use of Deadline to avoid calling System.nanoTime too often
-rw-r--r-- | src/library/scala/concurrent/Awaitable.scala | 22 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Promise.scala | 30 | ||||
-rw-r--r-- | src/library/scala/concurrent/package.scala | 34 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/TestUtil.scala | 12 | ||||
-rw-r--r-- | test/files/jvm/scala-concurrent-tck.scala | 9 |
5 files changed, 65 insertions, 42 deletions
diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala index 2205dd9869..655115349a 100644 --- a/src/library/scala/concurrent/Awaitable.scala +++ b/src/library/scala/concurrent/Awaitable.scala @@ -16,20 +16,34 @@ import scala.concurrent.util.Duration trait Awaitable[+T] { /** - * Should throw [[scala.concurrent.TimeoutException]] if it times out + * Await the "resolved" state of this Awaitable. * This method should not be called directly. * - * @throws InterruptedException if the wait call was interrupted + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the Awaitable itself + * @throws InterruptedException if the wait call was interrupted + * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready + * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]] */ @throws(classOf[TimeoutException]) @throws(classOf[InterruptedException]) def ready(atMost: Duration)(implicit permit: CanAwait): this.type /** - * Throws exceptions if it cannot produce a T within the specified time. + * Await and return the result of this Awaitable, which is either of type T or a thrown exception (any Throwable). * This method should not be called directly. * - * @throws InterruptedException if the wait call was interrupted + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the value if the Awaitable was successful within the specific maximum wait time + * @throws InterruptedException if the wait call was interrupted + * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready + * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]] */ @throws(classOf[Exception]) def result(atMost: Duration)(implicit permit: CanAwait): T diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 35aac974ec..f7ab85dc0c 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -12,7 +12,7 @@ package scala.concurrent.impl import java.util.concurrent.TimeUnit.NANOSECONDS import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, TimeoutException, ExecutionException } -import scala.concurrent.util.Duration +import scala.concurrent.util.{ Duration, Deadline } import scala.annotation.tailrec import scala.util.control.NonFatal import scala.util.{ Try, Success, Failure } @@ -64,16 +64,14 @@ private[concurrent] object Promise { protected final def tryAwait(atMost: Duration): Boolean = { @tailrec - def awaitUnsafe(waitTimeNanos: Long): Boolean = { - if (!isCompleted && waitTimeNanos > 0) { - val ms = NANOSECONDS.toMillis(waitTimeNanos) - val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec - val start = System.nanoTime() - synchronized { - if (!isCompleted) wait(ms, ns) // previously - this was a `while`, ending up in an infinite loop - } + def awaitUnsafe(deadline: Deadline, nextWait: Duration): Boolean = { + if (!isCompleted && nextWait > Duration.Zero) { + val ms = nextWait.toMillis + val ns = (nextWait.toNanos % 1000000l).toInt // as per object.wait spec + + synchronized { if (!isCompleted) wait(ms, ns) } - awaitUnsafe(waitTimeNanos - (System.nanoTime() - start)) + awaitUnsafe(deadline, deadline.timeLeft) } else isCompleted } @@ -81,21 +79,19 @@ private[concurrent] object Promise { def awaitUnbounded(): Boolean = { if (isCompleted) true else { - synchronized { - if (!isCompleted) wait() - } + synchronized { if (!isCompleted) wait() } awaitUnbounded() } } - if (atMost <= Duration.Zero) - isCompleted - else if (atMost eq Duration.Undefined) + if (atMost eq Duration.Undefined) throw new IllegalArgumentException("cannot wait for Undefined period") + else if (atMost <= Duration.Zero) + isCompleted else if (atMost == Duration.Inf) awaitUnbounded() else - awaitUnsafe(atMost.toNanos) + awaitUnsafe(atMost.fromNow, atMost) } @throws(classOf[TimeoutException]) diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 3681109653..1d06341d4d 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -67,14 +67,19 @@ package concurrent { */ object Await { /** + * Await the "resolved" state of this Awaitable. * Invokes ready() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * ready() blocks until the awaitable has completed or the timeout expires. * - * Throws a TimeoutException if the timeout expires, as that is in the contract of `Awaitable.ready`. - * @param awaitable the `Awaitable` on which `ready` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.ready` which is defined to be the awaitable itself. - * @throws InterruptedException if the wait call was interrupted + * @param awaitable + * the `Awaitable` on which `ready` is to be called + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the awaitable itself + * @throws InterruptedException if the wait call was interrupted + * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready + * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]] */ @throws(classOf[TimeoutException]) @throws(classOf[InterruptedException]) @@ -82,14 +87,19 @@ package concurrent { blocking(awaitable.ready(atMost)(AwaitPermission)) /** + * Await and return the result of this Awaitable, which is either of type T or a thrown exception (any Throwable). * Invokes result() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * result() blocks until the awaitable has completed or the timeout expires. * - * Throws a TimeoutException if the timeout expires, or any exception thrown by `Awaitable.result`. - * @param awaitable the `Awaitable` on which `result` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.result` - * @throws InterruptedException if the wait call was interrupted + * @param awaitable + * the `Awaitable` on which `result` is to be called + * @param atMost + * maximum wait time, which may be negative (no waiting is done), + * [[Duration.Inf]] for unbounded waiting, or a finite positive + * duration + * @return the value if the Awaitable was successful within the specific maximum wait time + * @throws InterruptedException if the wait call was interrupted + * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready + * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]] */ @throws(classOf[Exception]) def result[T](awaitable: Awaitable[T], atMost: Duration): T = diff --git a/src/partest/scala/tools/partest/TestUtil.scala b/src/partest/scala/tools/partest/TestUtil.scala index b86a8e2c7f..146e6fc69f 100644 --- a/src/partest/scala/tools/partest/TestUtil.scala +++ b/src/partest/scala/tools/partest/TestUtil.scala @@ -1,5 +1,7 @@ package scala.tools.partest +import reflect.{ classTag, ClassTag } + trait TestUtil { /** Given function and block of code, evaluates code block, * calls function with nanoseconds elapsed, and returns block result. @@ -29,8 +31,16 @@ trait TestUtil { assert(mult <= acceptableMultiple, "Performance difference too great: multiple = " + mult) } + + def intercept[T <: Exception : ClassTag](code: => Unit): Unit = + try { + code + assert(false, "did not throw " + classTag[T]) + } catch { + case ex: Exception if classTag[T].runtimeClass isInstance ex => + } } object TestUtil extends TestUtil { -}
\ No newline at end of file +} diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index a60f3c8a63..0e76b711de 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -12,6 +12,7 @@ import scala.concurrent.{ future, promise, blocking } import scala.util.{ Try, Success, Failure } import scala.concurrent.util.Duration import scala.reflect.{ classTag, ClassTag } +import scala.tools.partest.TestUtil.intercept trait TestBase { @@ -21,14 +22,6 @@ trait TestBase { sv.take(2000) } - def intercept[T <: Exception : ClassTag](code: => Unit): Unit = - try { - code - assert(false, "did not throw " + classTag[T]) - } catch { - case ex: Exception if classTag[T].runtimeClass isInstance ex => - } - // def assert(cond: => Boolean) { // try { // Predef.assert(cond) |