Path: blob/master/test/jdk/java/util/Collections/RacingCollections.java
41149 views
/*1* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 6360946 636094826* @summary Test various operations on concurrently mutating collections27* @author Martin Buchholz28*/2930import java.util.ArrayDeque;31import java.util.ArrayList;32import java.util.Collection;33import java.util.Collections;34import java.util.Comparator;35import java.util.Deque;36import java.util.HashMap;37import java.util.HashSet;38import java.util.Hashtable;39import java.util.LinkedList;40import java.util.List;41import java.util.Map;42import java.util.Queue;43import java.util.Set;44import java.util.TreeMap;45import java.util.TreeSet;46import java.util.Vector;47import java.util.concurrent.ArrayBlockingQueue;48import java.util.concurrent.ConcurrentHashMap;49import java.util.concurrent.ConcurrentLinkedDeque;50import java.util.concurrent.ConcurrentLinkedQueue;51import java.util.concurrent.ConcurrentSkipListMap;52import java.util.concurrent.ConcurrentSkipListSet;53import java.util.concurrent.CopyOnWriteArrayList;54import java.util.concurrent.CopyOnWriteArraySet;55import java.util.concurrent.LinkedBlockingDeque;56import java.util.concurrent.LinkedBlockingQueue;57import java.util.concurrent.LinkedTransferQueue;5859import static java.util.Collections.asLifoQueue;60import static java.util.Collections.checkedList;61import static java.util.Collections.checkedMap;62import static java.util.Collections.checkedSet;63import static java.util.Collections.newSetFromMap;64import static java.util.Collections.synchronizedList;65import static java.util.Collections.synchronizedMap;66import static java.util.Collections.synchronizedSet;67import static java.util.Collections.unmodifiableList;68import static java.util.Collections.unmodifiableMap;69import static java.util.Collections.unmodifiableSet;7071public class RacingCollections {72/**73* How long to run each "race" (in milliseconds).74* Turn this up to some higher value like 1000 for stress testing:75* java -Dmillis=1000 RacingCollections76*/77static final long defaultWorkTimeMillis = Long.getLong("millis", 10L);7879/**80* Whether to print debug information.81*/82static final boolean debug = Boolean.getBoolean("debug");8384static final Integer one = 1;85static final Integer two = 2;8687/**88* A thread that mutates an object forever, alternating between89* being empty and containing singleton "two"90*/91static class Frobber extends CheckedThread {92volatile boolean done = false;93boolean keepGoing(int i) { return (i % 128 != 0) || ! done; }9495final Object elLoco;96Frobber(Object elLoco) {97this.elLoco = elLoco;98this.start();99}100101@SuppressWarnings("unchecked")102void clear(Object o) {103if (o instanceof Collection)104((Collection<?>)o).clear();105else106((Map<?,?>)o).clear();107}108109@SuppressWarnings("unchecked")110void realRun() {111// Mutate elLoco wildly forever, checking occasionally for "done"112clear(elLoco);113if (elLoco instanceof List) {114List<Integer> l = (List<Integer>) elLoco;115for (int i = 0; keepGoing(i); i++) {116switch (i%2) {117case 0: l.add(two); break;118case 1: l.add(0, two); break;119}120switch (i%2) {121case 0: l.remove(two); break;122case 1: l.remove(0); break;123}}}124else if (elLoco instanceof Deque) {125Deque<Integer> q = (Deque<Integer>) elLoco;126for (int i = 0; keepGoing(i); i++) {127switch (i%6) {128case 0: q.add(two); break;129case 1: q.addFirst(two); break;130case 2: q.addLast(two); break;131case 3: q.offer(two); break;132case 4: q.offerFirst(two); break;133case 5: q.offerLast(two); break;134}135switch (i%6) {136case 0: q.remove(two); break;137case 1: q.removeFirst(); break;138case 2: q.removeLast(); break;139case 3: q.poll(); break;140case 4: q.pollFirst(); break;141case 5: q.pollLast(); break;142}}}143else if (elLoco instanceof Queue) {144Queue<Integer> q = (Queue<Integer>) elLoco;145for (int i = 0; keepGoing(i); i++) {146switch (i%2) {147case 0: q.add(two); break;148case 1: q.offer(two); break;149}150switch (i%2) {151case 0: q.remove(two); break;152case 1: q.poll(); break;153}}}154else if (elLoco instanceof Map) {155Map<Integer, Boolean> m = (Map<Integer, Boolean>) elLoco;156for (int i = 0; keepGoing(i); i++) {157m.put(two, true);158m.remove(two);159}}160else if (elLoco instanceof Collection) {161Collection<Integer> c = (Collection<Integer>) elLoco;162for (int i = 0; keepGoing(i); i++) {163c.add(two);164c.remove(two);165}}166else { throw new Error("Huh? " + elLoco); }167}168169void enoughAlready() {170done = true;171try { join(); } catch (Throwable t) { unexpected(t); }172}173}174175private static void checkEqualSanity(Object theRock, Object elLoco) {176//equal(theRock, theRock);177equal(elLoco, elLoco);178179// It would be nice someday to have theRock and elLoco never "equal",180// although the meaning of "equal" for mutating collections181// is a bit fuzzy. Uncomment when/if we fix:182// 6374942: Improve thread safety of collection .equals() methods183//notEqual(theRock, elLoco);184//notEqual(elLoco, theRock);185186notEqual(theRock.toString(), elLoco.toString());187}188189static class Looper {190final long quittingTime;191int i = 0;192Looper() { this(defaultWorkTimeMillis); }193Looper(long workTimeMillis) {194quittingTime = System.nanoTime() + workTimeMillis * 1024 * 1024;195}196boolean keepGoing() {197return (i++ % 128 != 0) || (System.nanoTime() - quittingTime < 0);198}199}200201private static void frob(Object theRock, Object elLoco) {202Frobber frobber = new Frobber(elLoco);203try {204if (theRock instanceof Collection) {205@SuppressWarnings("unchecked")206Collection<Integer> c = (Collection<Integer>) theRock;207if (! c.contains(one))208c.add(one);209} else {210@SuppressWarnings("unchecked")211Map<Integer, Boolean> m = (Map<Integer, Boolean>) theRock;212if (! m.containsKey(one))213m.put(one, true);214}215for (Looper looper = new Looper(); looper.keepGoing(); )216checkEqualSanity(theRock, elLoco);217}218catch (Throwable t) { unexpected(t); }219finally { frobber.enoughAlready(); }220}221222private static List<Map<Integer, Boolean>> newConcurrentMaps() {223List<Map<Integer, Boolean>> list = new ArrayList<>();224list.add(new ConcurrentHashMap<Integer, Boolean>());225list.add(new ConcurrentSkipListMap<Integer, Boolean>());226return list;227}228229private static List<Map<Integer, Boolean>> maps() {230List<Map<Integer, Boolean>> list = newConcurrentMaps();231list.add(new Hashtable<Integer, Boolean>());232list.add(new HashMap<Integer, Boolean>());233list.add(new TreeMap<Integer, Boolean>());234Comparator<Integer> cmp = new Comparator<>() {235public int compare(Integer x, Integer y) {236return x - y;237}};238list.add(new TreeMap<Integer, Boolean>(Collections.reverseOrder(cmp)));239return list;240}241242private static List<Set<Integer>> newConcurrentSets() {243List<Set<Integer>> list = new ArrayList<>();244list.add(new ConcurrentSkipListSet<Integer>());245list.add(new CopyOnWriteArraySet<Integer>());246return list;247}248249private static List<Set<Integer>> newSets() {250List<Set<Integer>> list = newConcurrentSets();251list.add(new HashSet<Integer>());252list.add(new TreeSet<Integer>());253list.add(new TreeSet<Integer>(Collections.reverseOrder()));254return list;255}256257private static List<List<Integer>> newConcurrentLists() {258List<List<Integer>> list = new ArrayList<>();259list.add(new CopyOnWriteArrayList<Integer>());260return list;261}262263private static List<List<Integer>> newLists() {264List<List<Integer>> list = newConcurrentLists();265list.add(new Vector<Integer>());266list.add(new ArrayList<Integer>());267return list;268}269270private static List<Queue<Integer>> newConcurrentQueues() {271List<Queue<Integer>> list = new ArrayList<>(newConcurrentDeques());272list.add(new ArrayBlockingQueue<Integer>(10));273list.add(new LinkedBlockingQueue<Integer>(10));274list.add(new LinkedTransferQueue<Integer>());275list.add(new ConcurrentLinkedQueue<Integer>());276return list;277}278279private static List<Queue<Integer>> newQueues() {280List<Queue<Integer>> list = new ArrayList<>(newDeques());281list.add(new LinkedBlockingQueue<Integer>(10));282return list;283}284285private static List<Deque<Integer>> newConcurrentDeques() {286List<Deque<Integer>> list = new ArrayList<>();287list.add(new LinkedBlockingDeque<Integer>(10));288list.add(new ConcurrentLinkedDeque<Integer>());289return list;290}291292private static List<Deque<Integer>> newDeques() {293List<Deque<Integer>> list = newConcurrentDeques();294list.add(new ArrayDeque<Integer>());295list.add(new LinkedList<Integer>());296return list;297}298299private static void describe(Class<?> k, Object x, Object y) {300if (debug)301System.out.printf("%s: %s, %s%n", k.getSimpleName(),302x.getClass().getSimpleName(),303y.getClass().getSimpleName());304}305306private static void realMain(String[] args) {307for (Map<Integer, Boolean> x : maps())308for (Map<Integer, Boolean> y : newConcurrentMaps()) {309describe(Map.class, x, y);310x.put(one, true);311frob(x, y);312frob(unmodifiableMap(x), y);313frob(synchronizedMap(x), y);314frob(x, synchronizedMap(y));315frob(checkedMap(x, Integer.class, Boolean.class), y);316frob(x, checkedMap(y, Integer.class, Boolean.class));317x.clear();318frob(newSetFromMap(x), newSetFromMap(y));319frob(x.keySet(), newSetFromMap(y));320}321322for (Set<Integer> x : newSets())323for (Set<Integer> y : newConcurrentSets()) {324describe(Set.class, x, y);325frob(x, y);326frob(unmodifiableSet(x), y);327frob(synchronizedSet(x), y);328frob(x, synchronizedSet(y));329frob(checkedSet(x, Integer.class), y);330frob(x, checkedSet(y, Integer.class));331}332333for (List<Integer> x : newLists())334for (List<Integer> y : newConcurrentLists()) {335describe(List.class, x, y);336frob(x, y);337frob(unmodifiableList(x), y);338frob(synchronizedList(x), y);339frob(x, synchronizedList(y));340frob(checkedList(x, Integer.class), y);341frob(x, checkedList(y, Integer.class));342}343344for (Queue<Integer> x : newQueues())345for (Queue<Integer> y : newConcurrentQueues()) {346describe(Queue.class, x, y);347frob(x, y);348}349350for (Deque<Integer> x : newDeques())351for (Deque<Integer> y : newConcurrentDeques()) {352describe(Deque.class, x, y);353frob(asLifoQueue(x), y);354frob(x, asLifoQueue(y));355}356}357358//--------------------- Infrastructure ---------------------------359static volatile int passed = 0, failed = 0;360static void pass() {passed++;}361static void fail() {failed++; Thread.dumpStack();}362static void fail(String msg) {System.out.println(msg); fail();}363static void unexpected(Throwable t) {failed++; t.printStackTrace();}364static void check(boolean cond) {if (cond) pass(); else fail();}365static String toString(Object x) {366return ((x instanceof Collection) || (x instanceof Map)) ?367x.getClass().getName() : x.toString();}368static void equal(Object x, Object y) {369if (x == null ? y == null : x.equals(y)) pass();370else fail(toString(x) + " not equal to " + toString(y));}371static void notEqual(Object x, Object y) {372if (x == null ? y == null : x.equals(y))373fail(toString(x) + " equal to " + toString(y));374else pass();}375public static void main(String[] args) throws Throwable {376try {realMain(args);} catch (Throwable t) {unexpected(t);}377System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);378if (failed > 0) throw new AssertionError("Some tests failed");}379private abstract static class CheckedThread extends Thread {380abstract void realRun() throws Throwable;381public void run() {382try { realRun(); } catch (Throwable t) { unexpected(t); }}}383}384385386