1

I have this situation:

  • method a: an implicit ec is created

  • method a: calls another method in a Future, i.e Future(anotherMethod). anotherMethod, and all its subsequent calls no longer have the ec from method a in scope.

Example code:

class Foo {
  private implicit val ec: ExecutionContextExecutor =
        ExecutionContext.fromExecutor(Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors()))

  private val anotherClass = new Bar()

  def methodA() = Future(anotherClass.anotherMethod())
}

I'm guessing, that any calls to .par, e.g someVector.par.map.().seq etc, from anotherMethod or any of its subsequent calls, will use the global execution context and not the custom one created in method a. Is my assumption correct?

Ali
  • 261,656
  • 265
  • 575
  • 769
  • It would be easier if you gave us some code rather than a verbal description. – Tim Sep 22 '18 at 11:23
  • @Tim I've added some quick code that should hopefully clarify. Let me know if anything's still unclear – Ali Sep 22 '18 at 11:29

1 Answers1

5

I'm guessing, that any calls to .par, e.g someVector.par.map.().seq etc, from anotherMethod or any of its subsequent calls, will use the global execution context and not the custom one created in method a. Is my assumption correct?

Let's split this answer in two. First, if you have any other methods in your call chain that will require an implicit ExecutionContext, they will get the one implicitly defined inside your top level methodA call.

Otherwise, the parallel collection design in Scala has no notion of an ExecutionContext, that is strictly a property of Future. The parallel collection library has a notion of a TaskSupport which is responsible for scheduling inside the parallel collection:

*  Parallel collections are modular in the way operations are scheduled. Each
*  parallel collection is parameterized with a task support object which is
*  responsible for scheduling and load-balancing tasks to processors.

So these parallel collections will not have anything to do with the ExecutionContext declared in Foo. You can, however, explicitly set them via the tasksupport setter:

val par = List(1,2,3,4).par
par.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(4))
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thanks. If you know a bit more about how the .par does its load balancing, that would be great. – Ali Sep 22 '18 at 13:36
  • Follow up question: If the top level `methodA` has an implicit ec, and it calls `anotherClass.methodB()` in a `future` block, but `methodB` doesn't require an implicit ec, but it calls `yetAnotherClass.methodC` which does require an implicit ec, then the ec from `methodA` won't be passed in and method b will have to provide its own, right? I.e, `methodB` doesn't know its being called from a future by `methodA` – Ali Sep 23 '18 at 04:00
  • 1
    @ClickUpvote Regarding your first question, it depends on the underlying thread pool being used. You can read more about the `ForkJoinPool` which is pretty standard and has good documentation. Regarding your second question, yes, `yetAnotherClass.methodC` shouldn't compile unless it has an `ExecutionContext` in scope, be it via an import (i.e. the global execution context defined in Scala's stdlib) or via an implicit parameter and passed from someone higher up the call chain. – Yuval Itzchakov Sep 23 '18 at 12:19