| /* | |
| * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * @test | |
| * @bug 4486658 8010293 | |
| * @summary thread safety of toArray methods of collection views | |
| * @author Martin Buchholz | |
| */ | |
| import java.util.List; | |
| import java.util.concurrent.CompletableFuture; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.concurrent.Executors; | |
| import java.util.concurrent.ThreadLocalRandom; | |
| import java.util.stream.Collectors; | |
| import java.util.stream.IntStream; | |
| public class ToArray { | |
| public static void main(String[] args) throws Throwable { | |
| final int runsPerTest = Integer.getInteger("jsr166.runsPerTest", 1); | |
| final int reps = 10 * runsPerTest; | |
| for (int i = reps; i--> 0; ) | |
| executeTest(); | |
| } | |
| static void executeTest() throws Throwable { | |
| try (var executor = Executors.newCachedThreadPool()) { | |
| final ConcurrentHashMap<Integer, Integer> m = new ConcurrentHashMap<>(); | |
| final ThreadLocalRandom rnd = ThreadLocalRandom.current(); | |
| final int nCPU = Runtime.getRuntime().availableProcessors(); | |
| final int minWorkers = 2; | |
| final int maxWorkers = Math.max(minWorkers, Math.min(32, nCPU)); | |
| final int nWorkers = rnd.nextInt(minWorkers, maxWorkers + 1); | |
| final int sizePerWorker = 1024; | |
| final int maxSize = nWorkers * sizePerWorker; | |
| // The foreman busy-checks that the size of the arrays obtained | |
| // from the keys and values views grows monotonically until it | |
| // reaches the maximum size. | |
| // NOTE: these size constraints are not specific to toArray and are | |
| // applicable to any form of traversal of the collection views | |
| CompletableFuture<?> foreman = CompletableFuture.runAsync(new Runnable() { | |
| private int prevSize = 0; | |
| private boolean checkProgress(Object[] a) { | |
| int size = a.length; | |
| if (size < prevSize || size > maxSize) | |
| throw new AssertionError( | |
| String.format("prevSize=%d size=%d maxSize=%d", | |
| prevSize, size, maxSize)); | |
| prevSize = size; | |
| return size == maxSize; | |
| } | |
| public void run() { | |
| Integer[] empty = new Integer[0]; | |
| for (; ; ) | |
| if (checkProgress(m.values().toArray()) | |
| & checkProgress(m.keySet().toArray()) | |
| & checkProgress(m.values().toArray(empty)) | |
| & checkProgress(m.keySet().toArray(empty))) | |
| return; | |
| } | |
| }, executor); | |
| // Each worker puts globally unique keys into the map | |
| List<CompletableFuture<?>> workers = | |
| IntStream.range(0, nWorkers) | |
| .mapToObj(w -> (Runnable) () -> { | |
| for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++) | |
| m.put(o + i, i); | |
| }) | |
| .map(r -> CompletableFuture.runAsync(r, executor)) | |
| .collect(Collectors.toList()); | |
| // Wait for workers and foreman to complete | |
| workers.forEach(CompletableFuture<?>::join); | |
| foreman.join(); | |
| } | |
| } | |
| } |