Shouldn't Future.apply return a failed future instead of throwing an exception?


#1

Hi everyone, I have a question about Futures.
I’ve just realized that Future { 1 } could throw an exception if the the Executor rejects the execution, for example if the underlying queue is bounded and is filled.
This shouldn’t return a Failed Future (e.g. Future(Failure(RejectedExecutionException))) instead of throwing the exception?
Something like

  override def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] = {
    val p = new DefaultPromise[S]()
    try {
      onComplete { result => p.complete(try f(result) catch { case NonFatal(t) => Failure(t) }) }
    catch {
       case e: RejectedExecutionException => p.failure(e)
    }
    p.future
  }

Or it is conceptually wrong?
Thanks.


#2

This is an interesting question. My take on it is, by constructing a Future, you’re expecting that the code passed to it is entered in a queue. If it fails to be entered into the queue, then that’s an immediate failure, not a future one.


#3

Thanks for your answer.
I agree with you that the RejectedExecutionException is indicating that the computation can’t be started and not an exception proper of the computation, but i guess we could also say that a computation that isn’t executed is also a computation that has failed, hence a Future containing a Failure won’t be totally wrong. That’s why I think this is a conceptual question, unless I’m missing something.
The benefit of returning a Failed Future will be that we shouldn’t deal with

try {
  Future { ... }
} catch {
  case e => // treat the exception
}

Although this is a rare case, but it was surprising to me that the expression Future { ... } could throw an Exception.


#4

I, too, am a little bit torn on the best way to deal with this.

I’m inclined to think that the Future should throw the exception immediately. I’m a strong believer in the policy that “Exceptions are for exceptional situations.” “The execution queue is totally busted” sounds exceptional enough to qualify for exception throwing, in my opinion.

It would not be hard to convince me I’m somewhat wrong on that, though. heh.


#5

It’s not that the queue is “busted”, it’s normal behavior, applying
backpressure. Unfortunately, it’s not very well implemented, as it simply
regurgitates what the Java Executor does.


#6

Oh. Hm. I hadn’t considered that as a backpressure medium. Normally when I see RejectedExecutionException I’m dealing with a situation where I’ve tried to enqueue something after the execution pool has been shut down or is in an otherwise unhealthy state.

That’s a pretty lame way to indicate backpressure. :expressionless: