1 |
jsr166 |
1.1 |
/* |
2 |
jsr166 |
1.3 |
* Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. |
3 |
jsr166 |
1.1 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
|
|
* |
5 |
|
|
* This code is free software; you can redistribute it and/or modify it |
6 |
|
|
* under the terms of the GNU General Public License version 2 only, as |
7 |
|
|
* published by the Free Software Foundation. |
8 |
|
|
* |
9 |
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
10 |
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 |
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
12 |
|
|
* version 2 for more details (a copy is included in the LICENSE file that |
13 |
|
|
* accompanied this code). |
14 |
|
|
* |
15 |
|
|
* You should have received a copy of the GNU General Public License version |
16 |
|
|
* 2 along with this work; if not, write to the Free Software Foundation, |
17 |
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
18 |
|
|
* |
19 |
jsr166 |
1.3 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
20 |
|
|
* or visit www.oracle.com if you need additional information or have any |
21 |
|
|
* questions. |
22 |
jsr166 |
1.1 |
*/ |
23 |
|
|
|
24 |
|
|
/* |
25 |
|
|
* @test |
26 |
|
|
* @bug 6360946 6360948 |
27 |
|
|
* @summary Test various operations on concurrently mutating collections |
28 |
|
|
* @author Martin Buchholz |
29 |
|
|
*/ |
30 |
|
|
|
31 |
|
|
import static java.util.Collections.*; |
32 |
|
|
import java.util.*; |
33 |
|
|
import java.util.concurrent.*; |
34 |
|
|
|
35 |
|
|
public class RacingCollections { |
36 |
|
|
/** |
37 |
|
|
* How long to run each "race" (in milliseconds). |
38 |
|
|
* Turn this up to some higher value like 1000 for stress testing: |
39 |
|
|
* java -Dmillis=1000 RacingCollections |
40 |
|
|
*/ |
41 |
jsr166 |
1.4 |
static final long defaultWorkTimeMillis = Long.getLong("millis", 10L); |
42 |
jsr166 |
1.1 |
|
43 |
|
|
/** |
44 |
|
|
* Whether to print debug information. |
45 |
|
|
*/ |
46 |
jsr166 |
1.4 |
static final boolean debug = Boolean.getBoolean("debug"); |
47 |
jsr166 |
1.1 |
|
48 |
jsr166 |
1.4 |
static final Integer one = 1; |
49 |
|
|
static final Integer two = 2; |
50 |
jsr166 |
1.1 |
|
51 |
|
|
/** |
52 |
|
|
* A thread that mutates an object forever, alternating between |
53 |
|
|
* being empty and containing singleton "two" |
54 |
|
|
*/ |
55 |
|
|
static class Frobber extends CheckedThread { |
56 |
|
|
volatile boolean done = false; |
57 |
|
|
boolean keepGoing(int i) { return (i % 128 != 0) || ! done; } |
58 |
|
|
|
59 |
|
|
final Object elLoco; |
60 |
|
|
Frobber(Object elLoco) { |
61 |
|
|
this.elLoco = elLoco; |
62 |
|
|
this.start(); |
63 |
|
|
} |
64 |
|
|
|
65 |
jsr166 |
1.5 |
@SuppressWarnings("unchecked") |
66 |
|
|
void clear(Object o) { |
67 |
jsr166 |
1.1 |
if (o instanceof Collection) |
68 |
|
|
((Collection<?>)o).clear(); |
69 |
|
|
else |
70 |
|
|
((Map<?,?>)o).clear(); |
71 |
|
|
} |
72 |
|
|
|
73 |
jsr166 |
1.5 |
@SuppressWarnings("unchecked") |
74 |
|
|
void realRun() { |
75 |
jsr166 |
1.1 |
// Mutate elLoco wildly forever, checking occasionally for "done" |
76 |
|
|
clear(elLoco); |
77 |
|
|
if (elLoco instanceof List) { |
78 |
|
|
List<Integer> l = (List<Integer>) elLoco; |
79 |
|
|
for (int i = 0; keepGoing(i); i++) { |
80 |
|
|
switch (i%2) { |
81 |
|
|
case 0: l.add(two); break; |
82 |
|
|
case 1: l.add(0, two); break; |
83 |
|
|
} |
84 |
|
|
switch (i%2) { |
85 |
|
|
case 0: l.remove(two); break; |
86 |
|
|
case 1: l.remove(0); break; |
87 |
|
|
}}} |
88 |
|
|
else if (elLoco instanceof Deque) { |
89 |
|
|
Deque<Integer> q = (Deque<Integer>) elLoco; |
90 |
|
|
for (int i = 0; keepGoing(i); i++) { |
91 |
|
|
switch (i%6) { |
92 |
|
|
case 0: q.add(two); break; |
93 |
|
|
case 1: q.addFirst(two); break; |
94 |
|
|
case 2: q.addLast(two); break; |
95 |
|
|
case 3: q.offer(two); break; |
96 |
|
|
case 4: q.offerFirst(two); break; |
97 |
|
|
case 5: q.offerLast(two); break; |
98 |
|
|
} |
99 |
|
|
switch (i%6) { |
100 |
|
|
case 0: q.remove(two); break; |
101 |
|
|
case 1: q.removeFirst(); break; |
102 |
|
|
case 2: q.removeLast(); break; |
103 |
|
|
case 3: q.poll(); break; |
104 |
|
|
case 4: q.pollFirst(); break; |
105 |
|
|
case 5: q.pollLast(); break; |
106 |
|
|
}}} |
107 |
|
|
else if (elLoco instanceof Queue) { |
108 |
|
|
Queue<Integer> q = (Queue<Integer>) elLoco; |
109 |
|
|
for (int i = 0; keepGoing(i); i++) { |
110 |
|
|
switch (i%2) { |
111 |
|
|
case 0: q.add(two); break; |
112 |
|
|
case 1: q.offer(two); break; |
113 |
|
|
} |
114 |
|
|
switch (i%2) { |
115 |
|
|
case 0: q.remove(two); break; |
116 |
|
|
case 1: q.poll(); break; |
117 |
|
|
}}} |
118 |
|
|
else if (elLoco instanceof Map) { |
119 |
|
|
Map<Integer, Boolean> m = (Map<Integer, Boolean>) elLoco; |
120 |
|
|
for (int i = 0; keepGoing(i); i++) { |
121 |
|
|
m.put(two, true); |
122 |
|
|
m.remove(two); |
123 |
|
|
}} |
124 |
|
|
else if (elLoco instanceof Collection) { |
125 |
|
|
Collection<Integer> c = (Collection<Integer>) elLoco; |
126 |
|
|
for (int i = 0; keepGoing(i); i++) { |
127 |
|
|
c.add(two); |
128 |
|
|
c.remove(two); |
129 |
|
|
}} |
130 |
|
|
else { throw new Error("Huh? " + elLoco); } |
131 |
|
|
} |
132 |
|
|
|
133 |
|
|
void enoughAlready() { |
134 |
|
|
done = true; |
135 |
|
|
try { join(); } catch (Throwable t) { unexpected(t); } |
136 |
|
|
} |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
private static void checkEqualSanity(Object theRock, Object elLoco) { |
140 |
|
|
//equal(theRock, theRock); |
141 |
|
|
equal(elLoco, elLoco); |
142 |
|
|
|
143 |
|
|
// It would be nice someday to have theRock and elLoco never "equal", |
144 |
|
|
// although the meaning of "equal" for mutating collections |
145 |
|
|
// is a bit fuzzy. Uncomment when/if we fix: |
146 |
|
|
// 6374942: Improve thread safety of collection .equals() methods |
147 |
|
|
//notEqual(theRock, elLoco); |
148 |
|
|
//notEqual(elLoco, theRock); |
149 |
|
|
|
150 |
|
|
notEqual(theRock.toString(), elLoco.toString()); |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
static class Looper { |
154 |
|
|
final long quittingTime; |
155 |
|
|
int i = 0; |
156 |
|
|
Looper() { this(defaultWorkTimeMillis); } |
157 |
|
|
Looper(long workTimeMillis) { |
158 |
|
|
quittingTime = System.nanoTime() + workTimeMillis * 1024 * 1024; |
159 |
|
|
} |
160 |
|
|
boolean keepGoing() { |
161 |
|
|
return (i++ % 128 != 0) || (System.nanoTime() < quittingTime); |
162 |
|
|
} |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
private static void frob(Object theRock, Object elLoco) { |
166 |
|
|
Frobber frobber = new Frobber(elLoco); |
167 |
|
|
try { |
168 |
|
|
if (theRock instanceof Collection) { |
169 |
|
|
@SuppressWarnings("unchecked") |
170 |
|
|
Collection<Integer> c = (Collection<Integer>) theRock; |
171 |
|
|
if (! c.contains(one)) |
172 |
|
|
c.add(one); |
173 |
|
|
} else { |
174 |
|
|
@SuppressWarnings("unchecked") |
175 |
|
|
Map<Integer, Boolean> m = (Map<Integer, Boolean>) theRock; |
176 |
|
|
if (! m.containsKey(one)) |
177 |
|
|
m.put(one, true); |
178 |
|
|
} |
179 |
|
|
for (Looper looper = new Looper(); looper.keepGoing(); ) |
180 |
|
|
checkEqualSanity(theRock, elLoco); |
181 |
|
|
} |
182 |
|
|
catch (Throwable t) { unexpected(t); } |
183 |
|
|
finally { frobber.enoughAlready(); } |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
private static List<Map<Integer, Boolean>> newConcurrentMaps() { |
187 |
|
|
List<Map<Integer, Boolean>> list = |
188 |
|
|
new ArrayList<Map<Integer, Boolean>>(); |
189 |
|
|
list.add(new ConcurrentHashMap<Integer, Boolean>()); |
190 |
|
|
list.add(new ConcurrentSkipListMap<Integer, Boolean>()); |
191 |
|
|
return list; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
private static List<Map<Integer, Boolean>> maps() { |
195 |
|
|
List<Map<Integer, Boolean>> list = newConcurrentMaps(); |
196 |
|
|
list.add(new Hashtable<Integer, Boolean>()); |
197 |
|
|
list.add(new HashMap<Integer, Boolean>()); |
198 |
|
|
list.add(new TreeMap<Integer, Boolean>()); |
199 |
|
|
Comparator<Integer> cmp = new Comparator<Integer>() { |
200 |
|
|
public int compare(Integer x, Integer y) { |
201 |
|
|
return x - y; |
202 |
|
|
}}; |
203 |
|
|
list.add(new TreeMap<Integer, Boolean>(Collections.reverseOrder(cmp))); |
204 |
|
|
return list; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
private static List<Set<Integer>> newConcurrentSets() { |
208 |
|
|
List<Set<Integer>> list = new ArrayList<Set<Integer>>(); |
209 |
|
|
list.add(new ConcurrentSkipListSet<Integer>()); |
210 |
|
|
list.add(new CopyOnWriteArraySet<Integer>()); |
211 |
|
|
return list; |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
private static List<Set<Integer>> newSets() { |
215 |
|
|
List<Set<Integer>> list = newConcurrentSets(); |
216 |
|
|
list.add(new HashSet<Integer>()); |
217 |
|
|
list.add(new TreeSet<Integer>()); |
218 |
|
|
list.add(new TreeSet<Integer>(Collections.reverseOrder())); |
219 |
|
|
return list; |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
private static List<List<Integer>> newConcurrentLists() { |
223 |
|
|
List<List<Integer>> list = new ArrayList<List<Integer>>(); |
224 |
|
|
list.add(new CopyOnWriteArrayList<Integer>()); |
225 |
|
|
return list; |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
private static List<List<Integer>> newLists() { |
229 |
|
|
List<List<Integer>> list = newConcurrentLists(); |
230 |
|
|
list.add(new Vector<Integer>()); |
231 |
|
|
list.add(new ArrayList<Integer>()); |
232 |
|
|
return list; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
private static List<Queue<Integer>> newConcurrentQueues() { |
236 |
|
|
List<Queue<Integer>> list = |
237 |
|
|
new ArrayList<Queue<Integer>>(newConcurrentDeques()); |
238 |
|
|
list.add(new LinkedBlockingQueue<Integer>(10)); |
239 |
|
|
list.add(new LinkedTransferQueue<Integer>()); |
240 |
jsr166 |
1.2 |
list.add(new ConcurrentLinkedQueue<Integer>()); |
241 |
jsr166 |
1.1 |
return list; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
private static List<Queue<Integer>> newQueues() { |
245 |
|
|
List<Queue<Integer>> list = |
246 |
|
|
new ArrayList<Queue<Integer>>(newDeques()); |
247 |
|
|
list.add(new LinkedBlockingQueue<Integer>(10)); |
248 |
|
|
return list; |
249 |
|
|
} |
250 |
|
|
|
251 |
|
|
private static List<Deque<Integer>> newConcurrentDeques() { |
252 |
|
|
List<Deque<Integer>> list = new ArrayList<Deque<Integer>>(); |
253 |
|
|
list.add(new LinkedBlockingDeque<Integer>(10)); |
254 |
jsr166 |
1.2 |
list.add(new ConcurrentLinkedDeque<Integer>()); |
255 |
jsr166 |
1.1 |
return list; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
private static List<Deque<Integer>> newDeques() { |
259 |
|
|
List<Deque<Integer>> list = newConcurrentDeques(); |
260 |
|
|
list.add(new ArrayDeque<Integer>()); |
261 |
|
|
list.add(new LinkedList<Integer>()); |
262 |
|
|
return list; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
private static void describe(Class<?> k, Object x, Object y) { |
266 |
|
|
if (debug) |
267 |
|
|
System.out.printf("%s: %s, %s%n", k.getSimpleName(), |
268 |
|
|
x.getClass().getSimpleName(), |
269 |
|
|
y.getClass().getSimpleName()); |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
private static void realMain(String[] args) { |
273 |
|
|
for (Map<Integer, Boolean> x : maps()) |
274 |
|
|
for (Map<Integer, Boolean> y : newConcurrentMaps()) { |
275 |
|
|
describe(Map.class, x, y); |
276 |
|
|
x.put(one, true); |
277 |
|
|
frob(x, y); |
278 |
|
|
frob(unmodifiableMap(x), y); |
279 |
|
|
frob(synchronizedMap(x), y); |
280 |
|
|
frob(x, synchronizedMap(y)); |
281 |
|
|
frob(checkedMap(x, Integer.class, Boolean.class), y); |
282 |
|
|
frob(x, checkedMap(y, Integer.class, Boolean.class)); |
283 |
|
|
x.clear(); |
284 |
|
|
frob(newSetFromMap(x), newSetFromMap(y)); |
285 |
|
|
frob(x.keySet(), newSetFromMap(y)); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
for (Set<Integer> x : newSets()) |
289 |
|
|
for (Set<Integer> y : newConcurrentSets()) { |
290 |
|
|
describe(Set.class, x, y); |
291 |
|
|
frob(x, y); |
292 |
|
|
frob(unmodifiableSet(x), y); |
293 |
|
|
frob(synchronizedSet(x), y); |
294 |
|
|
frob(x, synchronizedSet(y)); |
295 |
|
|
frob(checkedSet(x, Integer.class), y); |
296 |
|
|
frob(x, checkedSet(y, Integer.class)); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
for (List<Integer> x : newLists()) |
300 |
|
|
for (List<Integer> y : newConcurrentLists()) { |
301 |
|
|
describe(List.class, x, y); |
302 |
|
|
frob(x, y); |
303 |
|
|
frob(unmodifiableList(x), y); |
304 |
|
|
frob(synchronizedList(x), y); |
305 |
|
|
frob(x, synchronizedList(y)); |
306 |
|
|
frob(checkedList(x, Integer.class), y); |
307 |
|
|
frob(x, checkedList(y, Integer.class)); |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
for (Queue<Integer> x : newQueues()) |
311 |
|
|
for (Queue<Integer> y : newConcurrentQueues()) { |
312 |
|
|
describe(Queue.class, x, y); |
313 |
|
|
frob(x, y); |
314 |
|
|
} |
315 |
|
|
|
316 |
|
|
for (Deque<Integer> x : newDeques()) |
317 |
|
|
for (Deque<Integer> y : newConcurrentDeques()) { |
318 |
|
|
describe(Deque.class, x, y); |
319 |
|
|
frob(asLifoQueue(x), y); |
320 |
|
|
frob(x, asLifoQueue(y)); |
321 |
|
|
} |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
//--------------------- Infrastructure --------------------------- |
325 |
|
|
static volatile int passed = 0, failed = 0; |
326 |
|
|
static void pass() {passed++;} |
327 |
|
|
static void fail() {failed++; Thread.dumpStack();} |
328 |
|
|
static void fail(String msg) {System.out.println(msg); fail();} |
329 |
|
|
static void unexpected(Throwable t) {failed++; t.printStackTrace();} |
330 |
|
|
static void check(boolean cond) {if (cond) pass(); else fail();} |
331 |
|
|
static String toString(Object x) { |
332 |
|
|
return ((x instanceof Collection) || (x instanceof Map)) ? |
333 |
|
|
x.getClass().getName() : x.toString();} |
334 |
|
|
static void equal(Object x, Object y) { |
335 |
|
|
if (x == null ? y == null : x.equals(y)) pass(); |
336 |
|
|
else fail(toString(x) + " not equal to " + toString(y));} |
337 |
|
|
static void notEqual(Object x, Object y) { |
338 |
|
|
if (x == null ? y == null : x.equals(y)) |
339 |
|
|
fail(toString(x) + " equal to " + toString(y)); |
340 |
|
|
else pass();} |
341 |
|
|
public static void main(String[] args) throws Throwable { |
342 |
|
|
try {realMain(args);} catch (Throwable t) {unexpected(t);} |
343 |
|
|
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); |
344 |
|
|
if (failed > 0) throw new AssertionError("Some tests failed");} |
345 |
jsr166 |
1.4 |
private abstract static class CheckedThread extends Thread { |
346 |
jsr166 |
1.1 |
abstract void realRun() throws Throwable; |
347 |
|
|
public void run() { |
348 |
|
|
try { realRun(); } catch (Throwable t) { unexpected(t); }}} |
349 |
|
|
} |