java.lang.IllegalArgumentException: requirement failed from the concurrency library in Scala 2.12

389 views
Skip to first unread message

glapark

unread,
Nov 17, 2016, 4:53:23 AM11/17/16
to scala-internals
Hello,

I have some issue with the concurrency library in Scala 2.12. To be specific:

1) We would like to rethrow RejectedExecutionException if a Future fails to start because the ExecutionContext has been shut down already. For example,
def initialize(): Unit = {
  Future { 
    /* body */
  } (executionContext)
}
If executionContext has been shut down already and the body fails to run, initialize() should raise RejectedExecutionException.

2) We would like to swallow RejectedExecutionException if callbacks fail to start because the ExecutionContext has been shut down already. For example,
  intializeTask.future.foreach { 
    /* body */
  } (executionContext)
If executionContext has been shut down already and the body is not executed, it will be reported to RejectedExecutionHandler, which will swallow it.

Previously our code was running fine with Scala 2.11.8. We passed RejectedExecutionHandler as an argument to ThreadPoolExecutor, and rejectedExecutionReporter of type Throwable => Unit to ExecutionContext.fromExecutorService().

Now with Scala 2.12, I see the following error message occasionally, which should be due to rethrowing RejectedExecutionException in the ExecutionContext. 

Exception in thread "initializer-838-1" java.lang.IllegalStateException: problem in scala.concurrent internal callback
at scala.concurrent.Future$InternalCallbackExecutor$.reportFailure(Future.scala:866)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:68)
at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1(Promise.scala:284)
at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1$adapted(Promise.scala:284)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:284)
at scala.concurrent.Promise.complete(Promise.scala:49)
at scala.concurrent.Promise.complete$(Promise.scala:48)
at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:183)
at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:29)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:264)
at scala.concurrent.BatchingExecutor$Batch.run(BatchingExecutor.scala:51)
at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:864)
at scala.concurrent.BatchingExecutor$Batch.processBatch$1(BatchingExecutor.scala:72)
at scala.concurrent.BatchingExecutor$Batch.$anonfun$run$1(BatchingExecutor.scala:78)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:81)
at scala.concurrent.BatchingExecutor$Batch.run(BatchingExecutor.scala:55)
at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:864)
at scala.concurrent.BatchingExecutor.execute(BatchingExecutor.scala:106)
at scala.concurrent.BatchingExecutor.execute$(BatchingExecutor.scala:103)
at scala.concurrent.Future$InternalCallbackExecutor$.execute(Future.scala:862)
... 12 more

So, I wonder how I can implement the above 1) and 2) in Scala 2.12. I noticed that Scala 2.12 starts Future by passing the computation as a callback to unit, and is this the reason for all this?

Thanks a lot!

-- Gla Park

√iktor Klang

unread,
Nov 21, 2016, 8:12:33 AM11/21/16
to scala-internals
Hi Gla,

I don't really have time today to have any look at this at the moment, but I'll try to explain what could be happening.

With 2.12 the creation of Future via Future.apply has been streamlined to be equivalent of creating a new future through a transformation, ex:

Future("foo") == someSuccessfulFuture.map(_ => "foo")

The reason for this is important: there should be no semantical difference between the two.

Now, this means that handling errors the way you did with try-catch around "Future.apply" shouldn't have worked in the first place, but for some reason it used to.

Now, I haven't had a chance to have a look, but *ideally* the failure to submit a Runnable to the ExecutionContext should result in a failed downstream future, but I think/suspect that what is happening is that the context whether the callback is creating another promise or not is lost (type-wise at least) at the point where the callback is scheduled to be added to the ExecutionContext.

Does that make sense?

Cheers,

glapark

unread,
Nov 21, 2016, 8:41:52 AM11/21/16
to scala-internals
Thank you for your reply. As you pointed out, it is perhaps due to the use of the unit in creating Future and the ExecutionContext associated with unit.

Cheers,

--- Gla Park

Viktor Klang

unread,
Nov 21, 2016, 9:03:51 AM11/21/16
to scala-i...@googlegroups.com
I think there was a bit of a discrepancy how Future.apply was behaving in comparison to the transformations in version before 2.12.
As mentioned the new behavior is consistent and there shouldn't exist any observable difference between the two.

--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-internals+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



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