Is ExecutionContext.reportFailure working?

17 views
Skip to first unread message

Sean Shubin

unread,
Jun 19, 2017, 6:51:21 PM6/19/17
to Akka User List
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)
      }
    }

Output

    a
    b
    d
    Future(Failure(java.lang.RuntimeException: boo!))

Viktor Klang

unread,
Jun 20, 2017, 4:01:09 AM6/20/17
to Akka User List
Hi Sean,

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 akka-user+unsubscribe@googlegroups.com.
To post to this group, send email to akka...@googlegroups.com.
Visit this group at https://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.



--
Cheers,

Sean Shubin

unread,
Jun 20, 2017, 4:32:18 PM6/20/17
to Akka User List
Regarding #1, that is the information I was missing, thank you for clearing that up.  Hopefully that will guide me to how to test asynchronous code with an synchronous execution context.

Regarding #2 and #3, I agree, I was just trying to figure out what was going on.  The reportFailure behavior was surprising to me, so I was guarding against other surprises.
Hi Sean,

To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+...@googlegroups.com.

To post to this group, send email to akka...@googlegroups.com.
Visit this group at https://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.



--
Cheers,

Viktor Klang

unread,
Jun 20, 2017, 4:55:15 PM6/20/17
to Akka User List
happy hakking!

To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+unsubscribe@googlegroups.com.

To post to this group, send email to akka...@googlegroups.com.
Visit this group at https://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.



--
Cheers,
Reply all
Reply to author
Forward
0 new messages