Hi Sean,

On Tue, Jun 20, 2017 at 12:51 AM, Sean Shubin <[email protected]> wrote:

> While I was trying to stub out an execution context to test concurrent
> code, I noticed some surprising behavior demonstrated below.  I would have
> expected the exception thrown by the Future to cause reportFailure to be
> invoked.  To my surprise, reportFailure never got called at all, and the
> runnable.run() method carried on like nothing happened, even though an
> exception was thrown.  Obviously the code block run by the future is being
> wrapped by a runnable at some point, the surprising thing is that it seems
> to be handling the exception in a way that never notifies the reportFailure
> method.  I am having a hard time figuring it out from the source code, so
> was hoping someone more familiar with the scala.concurrent library could
> help me understand what is going on here.  My eventual goal is to be able
> to unit-test concurrent code by having tests take control of when different
> futures are launched and resolved.  That way I can resolve futures in
> different orders and make sure the code is correct no matter how the thread
> scheduler happens to fire off the futures.
>
>     import java.util.concurrent.TimeUnit
>
>     import org.scalatest.FunSuite
>
>     import scala.concurrent.duration.Duration
>     import scala.concurrent.{Await, ExecutionContext, Future}
>
>     class PrototypeForStubbingExecutionContext extends FunSuite {
>       test("foo") {
>         implicit val executionContext = new ExecutionContext {
>           override def execute(runnable: Runnable): Unit = {
>             println("a")
>             runnable.run()
>             println("d")
>           }
>
>           override def reportFailure(cause: Throwable): Unit = {
>             println("e")
>             cause.printStackTrace()
>             throw cause
>           }
>         }
>         val x = Future {
>           println("b")
>           throw new RuntimeException("boo!")
>           println("c")
>         }(executionContext)
>         Await.ready(x, Duration(1, TimeUnit.SECONDS))
>         println(x)
>       }
>     }
>

There are several issues with the code above,

1: `reportFailure` is only invoked if there is nowhere else to send the
exception, in the case of Future if it is possible to complete the end
result with the failure.

2: `reportFailure` should most definitely not throw exceptions, the only
reason it was invoked was that there was nowhere else to pass the original
exception, so rethrowing from it make little-to-no-sense at all.

3: Await.ready for logic executed on a synchronous EC seems superfluous?

You can see the `reportFailure` be invoked in for instance, `andThen`
throws an exception:

scala> Future(1).andThen({ case _ => throw null }).foreach(println)
java.lang.NullPointerException
at
$line11.$read$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1.applyOrElse(<console>:18)
at
$line11.$read$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1.applyOrElse(<console>:18)
at scala.concurrent.Future.$anonfun$andThen$1(Future.scala:531)
at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:29)
at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:29)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
at
scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:140)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at
java.util.concurrent.ForkJoinPool$WorkQueue.pollAndExecAll(ForkJoinPool.java:1021)
at
java.util.concurrent.ForkJoinPool$WorkQueue.execLocalTasks(ForkJoinPool.java:1046)
at
java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1058)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at
java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
1


>
> Output
>
>     a
>     b
>     d
>     Future(Failure(java.lang.RuntimeException: boo!))
>
> --
> >>>>>>>>>> Read the docs: http://akka.io/docs/
> >>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/
> current/additional/faq.html
> >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
> ---
> You received this message because you are subscribed to the Google Groups
> "Akka User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at https://groups.google.com/group/akka-user.
> For more options, visit https://groups.google.com/d/optout.
>



-- 
Cheers,
√

-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to