29 |
|
* @run main GCDuringIteration |
30 |
|
* @summary Check that iterators work properly in the presence of |
31 |
|
* concurrent finalization and removal of elements. |
32 |
< |
* @key randomness intermittent |
32 |
> |
* @key randomness |
33 |
|
*/ |
34 |
|
|
35 |
< |
import java.util.*; |
35 |
> |
import static java.util.concurrent.TimeUnit.SECONDS; |
36 |
> |
|
37 |
> |
import java.lang.ref.WeakReference; |
38 |
> |
import java.util.Arrays; |
39 |
> |
import java.util.Iterator; |
40 |
> |
import java.util.Map; |
41 |
> |
import java.util.NoSuchElementException; |
42 |
> |
import java.util.Random; |
43 |
> |
import java.util.WeakHashMap; |
44 |
|
import java.util.concurrent.CountDownLatch; |
45 |
+ |
import java.util.function.BooleanSupplier; |
46 |
|
import jdk.testlibrary.RandomFactory; |
47 |
|
|
48 |
|
public class GCDuringIteration { |
40 |
– |
private static void waitForFinalizersToRun() { |
41 |
– |
for (int i = 0; i < 2; i++) |
42 |
– |
tryWaitForFinalizersToRun(); |
43 |
– |
} |
49 |
|
|
50 |
< |
private static void tryWaitForFinalizersToRun() { |
51 |
< |
System.gc(); |
52 |
< |
final CountDownLatch fin = new CountDownLatch(1); |
53 |
< |
new Object() { protected void finalize() { fin.countDown(); }}; |
54 |
< |
System.gc(); |
55 |
< |
try { fin.await(); } |
56 |
< |
catch (InterruptedException ie) { throw new Error(ie); } |
50 |
> |
/** No guarantees, but effective in practice. */ |
51 |
> |
static void forceFullGc() { |
52 |
> |
CountDownLatch finalizeDone = new CountDownLatch(1); |
53 |
> |
WeakReference<?> ref = new WeakReference<Object>(new Object() { |
54 |
> |
protected void finalize() { finalizeDone.countDown(); }}); |
55 |
> |
try { |
56 |
> |
for (int i = 0; i < 10; i++) { |
57 |
> |
System.gc(); |
58 |
> |
if (finalizeDone.await(1L, SECONDS) && ref.get() == null) { |
59 |
> |
System.runFinalization(); // try to pick up stragglers |
60 |
> |
return; |
61 |
> |
} |
62 |
> |
} |
63 |
> |
} catch (InterruptedException unexpected) { |
64 |
> |
throw new AssertionError("unexpected InterruptedException"); |
65 |
> |
} |
66 |
> |
throw new AssertionError("failed to do a \"full\" gc"); |
67 |
> |
} |
68 |
> |
|
69 |
> |
static void gcAwait(BooleanSupplier s) { |
70 |
> |
for (int i = 0; i < 10; i++) { |
71 |
> |
if (s.getAsBoolean()) |
72 |
> |
return; |
73 |
> |
forceFullGc(); |
74 |
> |
} |
75 |
> |
throw new AssertionError("failed to satisfy condition"); |
76 |
|
} |
77 |
|
|
78 |
|
// A class with the traditional pessimal hashCode implementation, |
100 |
|
if (rnd.nextBoolean()) check(it.hasNext()); |
101 |
|
equal(it.next().getValue(), i); |
102 |
|
} |
103 |
< |
if (rnd.nextBoolean()) |
104 |
< |
THROWS(NoSuchElementException.class, |
105 |
< |
new F(){void f(){it.next();}}); |
103 |
> |
if (rnd.nextBoolean()) { |
104 |
> |
try { |
105 |
> |
it.next(); |
106 |
> |
throw new AssertionError("should throw"); |
107 |
> |
} catch (NoSuchElementException success) {} |
108 |
> |
} |
109 |
> |
|
110 |
|
if (rnd.nextBoolean()) |
111 |
|
check(! it.hasNext()); |
112 |
|
} |
134 |
|
int first = firstValue(map); |
135 |
|
final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); |
136 |
|
foos[first] = null; |
137 |
< |
for (int i = 0; i < 10 && map.size() != first; i++) |
110 |
< |
tryWaitForFinalizersToRun(); |
111 |
< |
equal(map.size(), first); |
137 |
> |
gcAwait(() -> map.size() == first); |
138 |
|
checkIterator(it, first-1); |
139 |
|
equal(map.size(), first); |
140 |
|
equal(firstValue(map), first-1); |
145 |
|
final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); |
146 |
|
it.next(); // protects first entry |
147 |
|
System.out.println(map.values()); |
148 |
+ |
int oldSize = map.size(); |
149 |
|
foos[first] = null; |
150 |
< |
tryWaitForFinalizersToRun(); |
151 |
< |
equal(map.size(), first+1); |
150 |
> |
forceFullGc(); |
151 |
> |
equal(map.size(), oldSize); |
152 |
|
System.out.println(map.values()); |
153 |
|
checkIterator(it, first-1); |
154 |
|
// first entry no longer protected |
155 |
< |
for (int i = 0; i < 10 && map.size() != first; i++) |
129 |
< |
tryWaitForFinalizersToRun(); |
130 |
< |
equal(map.size(), first); |
155 |
> |
gcAwait(() -> map.size() == first); |
156 |
|
equal(firstValue(map), first-1); |
157 |
|
} |
158 |
|
|
162 |
|
it.next(); // protects first entry |
163 |
|
System.out.println(map.values()); |
164 |
|
foos[first] = foos[first-1] = null; |
165 |
< |
tryWaitForFinalizersToRun(); |
141 |
< |
equal(map.size(), first); |
165 |
> |
gcAwait(() -> map.size() == first); |
166 |
|
equal(firstValue(map), first); |
167 |
|
System.out.println(map.values()); |
168 |
|
checkIterator(it, first-2); |
169 |
|
// first entry no longer protected |
170 |
< |
for (int i = 0; i < 10 && map.size() != first-1; i++) |
147 |
< |
tryWaitForFinalizersToRun(); |
148 |
< |
equal(map.size(), first-1); |
170 |
> |
gcAwait(() -> map.size() == first-1); |
171 |
|
equal(firstValue(map), first-2); |
172 |
|
} |
173 |
|
|
177 |
|
it.next(); // protects first entry |
178 |
|
it.hasNext(); // protects second entry |
179 |
|
System.out.println(map.values()); |
180 |
+ |
int oldSize = map.size(); |
181 |
|
foos[first] = foos[first-1] = null; |
182 |
< |
tryWaitForFinalizersToRun(); |
182 |
> |
forceFullGc(); |
183 |
> |
equal(map.size(), oldSize); |
184 |
|
equal(firstValue(map), first); |
161 |
– |
equal(map.size(), first+1); |
185 |
|
System.out.println(map.values()); |
186 |
|
checkIterator(it, first-1); |
187 |
|
// first entry no longer protected |
188 |
< |
for (int i = 0; i < 10 && map.size() != first-1; i++) |
166 |
< |
tryWaitForFinalizersToRun(); |
167 |
< |
equal(map.size(), first-1); |
188 |
> |
gcAwait(() -> map.size() == first-1); |
189 |
|
equal(firstValue(map), first-2); |
190 |
|
} |
191 |
|
|
194 |
|
final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); |
195 |
|
it.next(); // protects first entry |
196 |
|
System.out.println(map.values()); |
197 |
+ |
equal(map.size(), first+1); |
198 |
|
foos[first] = foos[first-1] = null; |
199 |
< |
tryWaitForFinalizersToRun(); |
199 |
> |
gcAwait(() -> map.size() == first); |
200 |
|
it.remove(); |
201 |
|
equal(firstValue(map), first-2); |
202 |
|
equal(map.size(), first-1); |
203 |
|
System.out.println(map.values()); |
204 |
|
checkIterator(it, first-2); |
205 |
|
// first entry no longer protected |
206 |
< |
for (int i = 0; i < 10 && map.size() != first-1; i++) |
185 |
< |
tryWaitForFinalizersToRun(); |
186 |
< |
equal(map.size(), first-1); |
206 |
> |
gcAwait(() -> map.size() == first-1); |
207 |
|
equal(firstValue(map), first-2); |
208 |
|
} |
209 |
|
|
214 |
|
it.remove(); |
215 |
|
it.hasNext(); // protects second entry |
216 |
|
System.out.println(map.values()); |
217 |
+ |
equal(map.size(), first); |
218 |
|
foos[first] = foos[first-1] = null; |
219 |
< |
tryWaitForFinalizersToRun(); |
219 |
> |
forceFullGc(); |
220 |
|
equal(firstValue(map), first-1); |
221 |
|
equal(map.size(), first); |
222 |
|
System.out.println(map.values()); |
223 |
|
checkIterator(it, first-1); |
224 |
< |
for (int i = 0; i < 10 && map.size() != first-1; i++) |
204 |
< |
tryWaitForFinalizersToRun(); |
205 |
< |
equal(map.size(), first-1); |
224 |
> |
gcAwait(() -> map.size() == first-1); |
225 |
|
equal(firstValue(map), first-2); |
226 |
|
} |
227 |
|
|
230 |
|
final Iterator<Map.Entry<Foo,Integer>> it = map.entrySet().iterator(); |
231 |
|
it.hasNext(); // protects first entry |
232 |
|
Arrays.fill(foos, null); |
233 |
< |
tryWaitForFinalizersToRun(); |
215 |
< |
equal(map.size(), 1); |
233 |
> |
gcAwait(() -> map.size() == 1); |
234 |
|
System.out.println(map.values()); |
235 |
|
equal(it.next().getValue(), first); |
236 |
|
check(! it.hasNext()); |
237 |
< |
for (int i = 0; i < 10 && map.size() != 0; i++) |
220 |
< |
tryWaitForFinalizersToRun(); |
221 |
< |
equal(map.size(), 0); |
237 |
> |
gcAwait(() -> map.size() == 0); |
238 |
|
check(map.isEmpty()); |
239 |
|
} |
240 |
|
} |
255 |
|
try {test(args);} catch (Throwable t) {unexpected(t);} |
256 |
|
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); |
257 |
|
if (failed > 0) throw new AssertionError("Some tests failed");} |
242 |
– |
abstract class F {abstract void f() throws Throwable;} |
243 |
– |
void THROWS(Class<? extends Throwable> k, F... fs) { |
244 |
– |
for (F f : fs) |
245 |
– |
try {f.f(); fail("Expected " + k.getName() + " not thrown");} |
246 |
– |
catch (Throwable t) { |
247 |
– |
if (k.isAssignableFrom(t.getClass())) pass(); |
248 |
– |
else unexpected(t);}} |
258 |
|
} |