diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2012-11-26 14:39:22 -0800 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2012-11-26 14:39:22 -0800 |
commit | 72f72811582af9a9c41dbc82a74d771198024a57 (patch) | |
tree | 275eddcf4ab55fb04e9c5224c6c1da69de6c0449 | |
parent | 3fd51865dfcb6121f84145f4504abd0f80bf6cca (diff) | |
parent | 4eb47ba34bf10bc483fa8f04a9f76708f9161317 (diff) | |
download | scala-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
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 } |