ParSeq tasksupport ignored

I ran across a surprising (to me) behavior this week: a parallel collection inside of a Future can use the Future’s execution context instead of any TaskSupport you explicitly set. To illustrate:

import scala.concurrent.ExecutionContext.Implicits.global

object ForkJoinTester {

  def main(args: Array[String]): Unit = {
    testForkJoinInner("ForkJoin(2)", new ForkJoinTaskSupport(new ForkJoinPool(2)))
    Await.ready(Future(
      testForkJoinInner("ForkJoin(2) in Future", new ForkJoinTaskSupport(new ForkJoinPool(2)))
    ), Duration.Inf)
    Await.ready(Future(
      testForkJoinInner("ForkJoin(1) in Future", new ForkJoinTaskSupport(new ForkJoinPool(1)))
    ), Duration.Inf)
    Await.ready(Future(
      testForkJoinInner("ThreadPool(2) in Future", new ExecutionContextTaskSupport(
        ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(2))
      ))
    ), Duration.Inf)
    sys.exit(0)
  }

  def testForkJoinInner(name: String, ts: TaskSupport): Unit = {
    @volatile var tids = Set.empty[Long]
    val p = Range(0, 10000).par
    p.tasksupport = ts
    p.foreach(_ => {
      tids += Thread.currentThread().getId
    })
    println(s"$name: ${tids.size} threads")
  }

}

The output:

ForkJoin(2): 2 threads
ForkJoin(2) in Future: 8 threads
ForkJoin(1) in Future: 1 threads
ThreadPool(2) in Future: 2 threads

As you can see, the ForkJoin(2) tasksupport is ignored when inside a Future, and the global context is used instead.

Is this expected? It seems wrong to me. I’m using Scala 2.11.

Thanks!

I’m pretty sure this is https://github.com/scala/scala/pull/6159, fixed in 2.12.

1 Like