[concurrency-interest] Issues with ForkJoin update

Denys Geert gdenys at yahoo.com
Fri Apr 16 11:26:53 EDT 2010


Hi concurreny folks,

We recently tried the new update for FJ and have some problems with it.

We have coded a rather canonical use case of FJ (see below), a top-level task which recursively spawns child tasks to calculate the number of "tiles" (as in image tiles). With
the previous FJ version it finishes within a second, with the new version
it hangs "forever".

What's probably atypical about our use case is that we limit the FJ
pool (setMaximumPoolSize). The reason is that we have some tasks that are quite heavy
resource users (memory wise) so we need to limit to number of concurrent
threads. This all worked brilliantly using "helpJoin" on our current
version of FJ. Switching to "join" does not seem like an option for us.

When I plug in the new version of FJ, it often fails with a StackOverflow and occasionally hangs (which also happens in combination with StackOverflowErrors which seem to be swallowed within FJ). The code below illustrates the hanging problem, at least in my environment (Linux 32bit, JDK 1.6.0_15) .

When connecting a profiler, most FJ pool threads are "scanning" (one thread was in awaitDone):

"ForkJoinPool-1-worker-1" daemon prio=10 tid=0x7f503c00 nid=0x6cac runnable [0x7f8a3000]
   java.lang.Thread.State: RUNNABLE
    at jsr166y.ForkJoinWorkerThread.scanWhileJoining(ForkJoinWorkerThread.java:909)
    at jsr166y.ForkJoinTask.quietlyHelpJoin(ForkJoinTask.java:836)
    at jsr166y.ForkJoinTask.helpJoin(ForkJoinTask.java:816)
    at CountingTaskTest$CountingTask.computeChildTiles(CountingTaskTest.java:57)
    at CountingTaskTest$CountingTask.compute(CountingTaskTest.java:41)
    at CountingTaskTest$CountingTask.compute(CountingTaskTest.java:1)
    at jsr166y.RecursiveTask.exec(RecursiveTask.java:64)
    at jsr166y.ForkJoinTask.tryExec(ForkJoinTask.java:242)
    at jsr166y.ForkJoinTask.quietlyHelpJoin(ForkJoinTask.java:842)
    at jsr166y.ForkJoinTask.helpJoin(ForkJoinTask.java:816)
    at CountingTaskTest$CountingTask.computeChildTiles(CountingTaskTest.java:57)
    at CountingTaskTest$CountingTask.compute(CountingTaskTest.java:41)
    at CountingTaskTest$CountingTask.compute(CountingTaskTest.java:1)
    at jsr166y.RecursiveTask.exec(RecursiveTask.java:64)
    ....

The stack trace is extremely long. In the previous version, I had the impression the stack trace was proportional to the depth of child tasks spawned. Now this does not seem to be the case anymore.

Any insights would be appreciated.

The code and test code follow:
--------------------
import java.util.Random;

import jsr166y.ForkJoinPool;
import jsr166y.RecursiveTask;

public class CountingTaskTest {
  
  public interface Tiles {
    public long tryComputeTileCount(int aLevel, long aRow, long aCol);
  }
  
  public static class CountingTask extends RecursiveTask<Long> {
    private final int fLevel;
    private final long fCol, fRow;

    private final Tiles fTiles;

    public CountingTask(int aLevel, long aRow, long aCol, Tiles aTiles) {
      fLevel = aLevel;
      fCol = aCol;
      fRow = aRow;
      fTiles = aTiles;
    }

    public Long compute() {
      long numTiles = fTiles.tryComputeTileCount(fLevel, fRow, fCol);
      if (numTiles != -1) {
        return numTiles;
      }
      else {
        return computeChildTiles() + 1;
      }
    }

    private long computeChildTiles() {
      CountingTask task00 = createChildTask(0, 0);
      CountingTask task10 = createChildTask(0, 1);
      CountingTask task01 = createChildTask(1, 0);
      CountingTask task11 = createChildTask(1, 1);

      task00.fork();
      task10.fork();
      task01.fork();
      task11.fork();

      long ret = 0L;
      ret += task00.helpJoin();
      ret += task10.helpJoin();
      ret += task01.helpJoin();
      ret += task11.helpJoin();
      return ret;
    }

    private CountingTask createChildTask(int fRowOffset, int fColumnOffset) {
      return new CountingTask(fLevel + 1, fRowOffset, fColumnOffset, fTiles);
    }

  }

  public static class SimulatedTiles implements Tiles {
    public long tryComputeTileCount(int aLevel, long aRow, long aCol) {
      long seed = 369019586650768L;
      final Random r = new Random(seed);
      if (r.nextInt(4) == 0 || aLevel > 7) {
        return 1;
      } else {
        return -1;
      }
    }
  }
  
  public static void main(String[] args) {
    Tiles tiles = new SimulatedTiles();
    int availableProcessors = 4;
    ForkJoinPool pool = new ForkJoinPool(availableProcessors);
    pool.setMaintainsParallelism(true);
    pool.setMaximumPoolSize(availableProcessors);
    pool.invoke(new CountingTask(0, 0, 0, tiles));
  }

}

----------



      


More information about the Concurrency-interest mailing list