[SOLVED] Java ForkJoinPool – Potentially dangerous stack overflow in ReservedStackAccess annotated method

Issue

I’ve been trying to run a simple example of a ForkJoinPool using a RecursiveTask but I keep getting this message:

Java HotSpot(TM) 64-Bit Server VM warning: Potentially dangerous stack overflow in ReservedStackAccess annotated method java.util.concurrent.locks.ReentrantLock$Sync.nonfairTryAcquire(I)Z [1]

The program seems to run normally if the number of elements in my list is 1000 or less but as soon I add more elements I get that message during the execution. Apparently when the list is set with 2000 elements, the first 1000 are processed correctly but the other 1000 are completely skipped. I thought that the error message might have halted the execution right before processing the last 1000, but the println statement is always reached so it seems that for some reasons the ForkJoinPool dos not want to venture into further forking.

public class Product {

    private String name;
    private BigDecimal price;

    public static List<Product> generateListProducts() {
        List<Product> listProducts = new ArrayList<>();
        String price = "10.00";
        for (int i = 0; i < 2000; i++) {
            listProducts.add(new Product(String.format("Product %d", i), new BigDecimal(price)));
        }
        return listProducts;
    }

    public Product(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public void incrementPrice(BigDecimal percentage) {
        if (percentage == null) {
            return;
        }

        // price = price * (1 + perc/100)
        price = price.multiply(BigDecimal.valueOf(1L).add(percentage.divide(BigDecimal.valueOf(100L)))).setScale(2, RoundingMode.HALF_UP);
    }
}
public class MyAction extends RecursiveAction {

    private List<Product> listProducts;
    private int start, end;
    private BigDecimal percIncrement;
    private static final long serialVersionUID = 1L;

    public MyAction(List<Product> listProducts, int start, int end, BigDecimal percIncrement) {
        this.listProducts = listProducts;
        this.start = start;
        this.end = end;
        this.percIncrement = percIncrement;
    }

    @Override
    protected void compute() {
        if (end - start > 500) {
            int middle = start + end / 2;
            MyAction t1 = new MyAction(listProducts, start, middle, percIncrement);
            MyAction t2 = new MyAction(listProducts, middle, end, percIncrement);
            invokeAll(t1, t2);
        } else {
            updatePrice();
        }
    }

    private void updatePrice() {
        Product p;
        for (int i = start; i < end; i++) {
            p = listProducts.get(i);
            p.incrementPrice(percIncrement);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        //Creating a list with 2.000 elements where each product has its price set at 10.00
        List<Product> listProducts = Product.generateListProducts();

        //Creating a RecursiveAction that increments every product's price by 20%
        MyAction action = new MyAction(listProducts, 0, listProducts.size(), new BigDecimal("20.00"));

        //Instancing a ForkJoinPool and executing the RecursiveAction
        ForkJoinPool pool = new ForkJoinPool();
        pool.execute(action);

        do {
            //Printing the current info every second until the RecursiveAction is completed
            System.out.printf("Thread Count: %d\n", pool.getActiveThreadCount());
            System.out.printf("Thread Steal: %d\n", pool.getStealCount());
            System.out.printf("Parallelism: %d\n\n", pool.getParallelism());
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (!action.isDone());

        //Shutting down the forkJoinPool to allow the program termination
        pool.shutdown();

        //Checking if every product has been incremented
        BigDecimal bdTest = new BigDecimal("12.00");
        System.out.println(listProducts.stream().filter(p -> !p.getPrice().equals(bdTest)).count());
    }
}

I’ve also checked my java configurations to see if they were set to some poor values, since it seemed that the JVM was concerned with some potentially dangerous stack overflow operations, but they seem fine.
Java CMD configs

Solution

I post this only because I couldn’t find anywhere the error message I was given. It might be useful to somebody else.

As @DuncG made me notice in the comments it was just a silly error. I simply split the range incorrectly thus making different threads work on the same set of data…

middle should not be set to the value of start + end / 2 => (start + end) / 2

Answered By – Dan

Answer Checked By – Senaida (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *