aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-11-26 14:39:22 -0800
committerJason Zaugg <jzaugg@gmail.com>2012-11-26 14:39:22 -0800
commit72f72811582af9a9c41dbc82a74d771198024a57 (patch)
tree275eddcf4ab55fb04e9c5224c6c1da69de6c0449
parent3fd51865dfcb6121f84145f4504abd0f80bf6cca (diff)
parent4eb47ba34bf10bc483fa8f04a9f76708f9161317 (diff)
downloadscala-async-72f72811582af9a9c41dbc82a74d771198024a57.tar.gz
scala-async-72f72811582af9a9c41dbc82a74d771198024a57.tar.bz2
scala-async-72f72811582af9a9c41dbc82a74d771198024a57.zip
Merge pull request #44 from phaller/topic/exception-handling
Topic/exception handling
-rw-r--r--src/main/scala/scala/async/ExprBuilder.scala29
-rw-r--r--src/main/scala/scala/async/TransformUtils.scala2
-rw-r--r--src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala61
-rw-r--r--src/test/scala/scala/async/run/futures/FutureSpec.scala34
4 files changed, 106 insertions, 20 deletions
diff --git a/src/main/scala/scala/async/ExprBuilder.scala b/src/main/scala/scala/async/ExprBuilder.scala
index 7b4ccb8..ef27672 100644
--- a/src/main/scala/scala/async/ExprBuilder.scala
+++ b/src/main/scala/scala/async/ExprBuilder.scala
@@ -30,7 +30,7 @@ private[async] final case class ExprBuilder[C <: Context, FS <: FutureSystem](c:
def mkHandlerCaseForState: CaseDef
- def mkOnCompleteHandler: Option[CaseDef] = None
+ def mkOnCompleteHandler[T: c.WeakTypeTag]: Option[CaseDef] = None
def stats: List[Tree]
@@ -75,13 +75,32 @@ private[async] final case class ExprBuilder[C <: Context, FS <: FutureSystem](c:
mkHandlerCase(state, stats :+ callOnComplete)
}
- override def mkOnCompleteHandler: Option[CaseDef] = {
+ override def mkOnCompleteHandler[T: c.WeakTypeTag]: Option[CaseDef] = {
val tryGetTree =
Assign(
Ident(awaitable.resultName),
TypeApply(Select(Select(Ident(name.tr), Try_get), newTermName("asInstanceOf")), List(TypeTree(awaitable.resultType)))
)
- Some(mkHandlerCase(state, List(tryGetTree, mkStateTree(nextState), mkResumeApply)))
+
+ /* if (tr.isFailure)
+ * result$async.complete(tr.asInstanceOf[Try[T]])
+ * else {
+ * <resultName> = tr.get.asInstanceOf[<resultType>]
+ * <nextState>
+ * <mkResumeApply>
+ * }
+ */
+ val ifIsFailureTree =
+ If(Select(Ident(name.tr), Try_isFailure),
+ futureSystemOps.completeProm[T](
+ c.Expr[futureSystem.Prom[T]](Ident(name.result)),
+ c.Expr[scala.util.Try[T]](
+ TypeApply(Select(Ident(name.tr), newTermName("asInstanceOf")),
+ List(TypeTree(weakTypeOf[scala.util.Try[T]]))))).tree,
+ Block(List(tryGetTree, mkStateTree(nextState), mkResumeApply): _*)
+ )
+
+ Some(mkHandlerCase(state, List(ifIsFailureTree)))
}
override val toString: String =
@@ -276,7 +295,7 @@ private[async] final case class ExprBuilder[C <: Context, FS <: FutureSystem](c:
trait AsyncBlock {
def asyncStates: List[AsyncState]
- def onCompleteHandler: Tree
+ def onCompleteHandler[T: c.WeakTypeTag]: Tree
def resumeFunTree[T]: Tree
}
@@ -320,7 +339,7 @@ private[async] final case class ExprBuilder[C <: Context, FS <: FutureSystem](c:
* resume()
* }
*/
- val onCompleteHandler: Tree = Match(Ident(name.state), initStates.flatMap(_.mkOnCompleteHandler).toList)
+ def onCompleteHandler[T: c.WeakTypeTag]: Tree = Match(Ident(name.state), initStates.flatMap(_.mkOnCompleteHandler[T]).toList)
/**
* def resume(): Unit = {
diff --git a/src/main/scala/scala/async/TransformUtils.scala b/src/main/scala/scala/async/TransformUtils.scala
index 5b1fcbe..7571f88 100644
--- a/src/main/scala/scala/async/TransformUtils.scala
+++ b/src/main/scala/scala/async/TransformUtils.scala
@@ -155,6 +155,8 @@ private[async] final case class TransformUtils[C <: Context](c: C) {
}
val Try_get = methodSym(reify((null: scala.util.Try[Any]).get))
+ val Try_isFailure = methodSym(reify((null: scala.util.Try[Any]).isFailure))
+
val TryClass = c.mirror.staticClass("scala.util.Try")
val TryAnyType = appliedType(TryClass.toType, List(definitions.AnyTpe))
val NonFatalClass = c.mirror.staticModule("scala.util.control.NonFatal")
diff --git a/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala b/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala
new file mode 100644
index 0000000..733ea01
--- /dev/null
+++ b/src/test/scala/scala/async/run/exceptions/ExceptionsSpec.scala
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Typesafe Inc. <http://www.typesafe.com>
+ */
+
+package scala.async
+package run
+package exceptions
+
+import scala.async.Async.{async, await}
+
+import scala.concurrent.{future, ExecutionContext, Await}
+import ExecutionContext.Implicits._
+import scala.concurrent.duration._
+import scala.reflect.ClassTag
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class ExceptionsSpec {
+
+ @Test
+ def `uncaught exception within async`() {
+ val fut = async { throw new Exception("problem") }
+ intercept[Exception] { Await.result(fut, 2.seconds) }
+ }
+
+ @Test
+ def `uncaught exception within async after await`() {
+ val base = future { "five!".length }
+ val fut = async {
+ val len = await(base)
+ throw new Exception(s"illegal length: $len")
+ }
+ intercept[Exception] { Await.result(fut, 2.seconds) }
+ }
+
+ @Test
+ def `await failing future within async`() {
+ val base = future[Int] { throw new Exception("problem") }
+ val fut = async {
+ val x = await(base)
+ x * 2
+ }
+ intercept[Exception] { Await.result(fut, 2.seconds) }
+ }
+
+ @Test
+ def `await failing future within async after await`() {
+ val base = future[Any] { "five!".length }
+ val fut = async {
+ val a = await(base.mapTo[Int]) // result: 5
+ val b = await((future { (a * 2).toString }).mapTo[Int]) // result: ClassCastException
+ val c = await(future { (7 * 2).toString }) // result: "14"
+ b + "-" + c
+ }
+ intercept[ClassCastException] { Await.result(fut, 2.seconds) }
+ }
+
+}
diff --git a/src/test/scala/scala/async/run/futures/FutureSpec.scala b/src/test/scala/scala/async/run/futures/FutureSpec.scala
index 7a486b5..01c8620 100644
--- a/src/test/scala/scala/async/run/futures/FutureSpec.scala
+++ b/src/test/scala/scala/async/run/futures/FutureSpec.scala
@@ -101,11 +101,12 @@ class FutureSpec {
b + "-" + c
}
- val future2 = for {
- a <- future0.mapTo[Int]
- b <- (future { (a * 2).toString }).mapTo[Int]
- c <- future { (7 * 2).toString }
- } yield b + "-" + c
+ val future2 = async {
+ val a = await(future0.mapTo[Int])
+ val b = await((future { (a * 2).toString }).mapTo[Int])
+ val c = await(future { (7 * 2).toString })
+ b + "-" + c
+ }
Await.result(future1, defaultTimeout) mustBe ("10-14")
//assert(checkType(future1, manifest[String]))
@@ -139,20 +140,23 @@ class FutureSpec {
@Test def `recover from exceptions`() {
val future1 = Future(5)
- val future2 = future1 map (_ / 0)
- val future3 = future2 map (_.toString)
-
- val future4 = future1 recover {
+ val future2 = async { await(future1) / 0 }
+ val future3 = async { await(future2).toString }
+
+ val future1Recovered = future1 recover {
case e: ArithmeticException => 0
- } map (_.toString)
+ }
+ val future4 = async { await(future1Recovered).toString }
- val future5 = future2 recover {
+ val future2Recovered = future2 recover {
case e: ArithmeticException => 0
- } map (_.toString)
+ }
+ val future5 = async { await(future2Recovered).toString }
- val future6 = future2 recover {
+ val future2Recovered2 = future2 recover {
case e: MatchError => 0
- } map (_.toString)
+ }
+ val future6 = async { await(future2Recovered2).toString }
val future7 = future3 recover {
case e: ArithmeticException => "You got ERROR"
@@ -527,7 +531,7 @@ class FutureSpec {
@Test def `should not throw when Await.ready`() {
val expected = try Success(5 / 0) catch { case a: ArithmeticException => Failure(a) }
- val f = future(5).map(_ / 0)
+ val f = async { await(future(5)) / 0 }
Await.ready(f, defaultTimeout).value.get.toString mustBe expected.toString
}