Skip to content

Commit 5d799d8

Browse files
Maxim Kartashevprrace
authored andcommitted
8292304: [REDO] JDK-8289208 Test DrawRotatedStringUsingRotatedFont.java occasionally crashes on MacOS
Reviewed-by: prr
1 parent 4f50316 commit 5d799d8

File tree

3 files changed

+136
-19
lines changed

3 files changed

+136
-19
lines changed

src/java.desktop/share/classes/sun/java2d/Disposer.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
import java.lang.ref.WeakReference;
3434
import java.security.AccessController;
3535
import java.security.PrivilegedAction;
36-
import java.util.ArrayList;
3736
import java.util.Hashtable;
37+
import java.util.concurrent.ConcurrentLinkedDeque;
3838

3939
/**
4040
* This class is used for registering and disposing the native
@@ -145,7 +145,7 @@ public void run() {
145145
Reference<?> obj = queue.remove();
146146
obj.clear();
147147
DisposerRecord rec = records.remove(obj);
148-
rec.dispose();
148+
safeDispose(rec);
149149
obj = null;
150150
rec = null;
151151
clearDeferredRecords();
@@ -164,21 +164,23 @@ public void run() {
164164
public static interface PollDisposable {
165165
};
166166

167-
private static ArrayList<DisposerRecord> deferredRecords = null;
167+
private static ConcurrentLinkedDeque<DisposerRecord> deferredRecords = new ConcurrentLinkedDeque<>();
168168

169-
private static void clearDeferredRecords() {
170-
if (deferredRecords == null || deferredRecords.isEmpty()) {
171-
return;
169+
private static void safeDispose(DisposerRecord rec) {
170+
try {
171+
rec.dispose();
172+
} catch (final Exception e) {
173+
System.out.println("Exception while disposing deferred rec.");
172174
}
173-
for (int i=0;i<deferredRecords.size(); i++) {
174-
try {
175-
DisposerRecord rec = deferredRecords.get(i);
176-
rec.dispose();
177-
} catch (Exception e) {
178-
System.out.println("Exception while disposing deferred rec.");
175+
}
176+
177+
private static void clearDeferredRecords() {
178+
while (!deferredRecords.isEmpty()) {
179+
final DisposerRecord rec = deferredRecords.pollFirst();
180+
if (rec != null) {
181+
safeDispose(rec);
179182
}
180183
}
181-
deferredRecords.clear();
182184
}
183185

184186
/*
@@ -211,18 +213,15 @@ public static void pollRemove() {
211213
obj.clear();
212214
DisposerRecord rec = records.remove(obj);
213215
if (rec instanceof PollDisposable) {
214-
rec.dispose();
216+
safeDispose(rec);
215217
obj = null;
216218
rec = null;
217219
} else {
218220
if (rec == null) { // shouldn't happen, but just in case.
219221
continue;
220222
}
221223
deferred++;
222-
if (deferredRecords == null) {
223-
deferredRecords = new ArrayList<DisposerRecord>(5);
224-
}
225-
deferredRecords.add(rec);
224+
deferredRecords.offerLast(rec);
226225
}
227226
}
228227
} catch (Exception e) {

test/jdk/java/awt/Graphics2D/DrawString/DrawRotatedStringUsingRotatedFont.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737

3838
/**
3939
* @test
40-
* @bug 8065373
40+
* @bug 8065373 8289208
41+
* @key headful
4142
* @summary Verifies that we get correct direction, when draw rotated string.
4243
* @author Sergey Bylokhov
4344
* @run main DrawRotatedStringUsingRotatedFont
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
3+
* 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+
* 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+
*/
23+
import java.util.LinkedList;
24+
import java.util.List;
25+
import java.util.concurrent.atomic.AtomicInteger;
26+
import javax.swing.SwingUtilities;
27+
28+
import sun.java2d.Disposer;
29+
import sun.java2d.DisposerRecord;
30+
31+
/**
32+
* @test
33+
* @bug 8289208
34+
* @summary Verifies Disposer robustness in a multi-threaded environment.
35+
* @run main/othervm -mx128m TestDisposerRace
36+
* @modules java.desktop/sun.java2d
37+
*/
38+
public final class TestDisposerRace {
39+
private static final AtomicInteger recordsCount = new AtomicInteger();
40+
private static volatile boolean disposerDone = false;
41+
42+
public static void main(String[] args) throws Exception {
43+
TestDisposerRace test = new TestDisposerRace();
44+
test.run();
45+
46+
checkRecordsCountIsSane();
47+
if (recordsCount.get() > 0) {
48+
throw new RuntimeException("Some records (" + recordsCount + ") have not been disposed");
49+
}
50+
}
51+
52+
TestDisposerRace() {
53+
addRecordsToDisposer(30_000);
54+
}
55+
56+
void run() throws Exception {
57+
generateOOME();
58+
for (int i = 0; i < 1000; ++i) {
59+
SwingUtilities.invokeAndWait(Disposer::pollRemove);
60+
if (i % 10 == 0) {
61+
// Adding records will race with the diposer trying to remove them
62+
addRecordsToDisposer(1000);
63+
}
64+
}
65+
66+
Disposer.addObjectRecord(new Object(), new FinalDisposerRecord());
67+
68+
while (!disposerDone) {
69+
generateOOME();
70+
}
71+
}
72+
73+
private static void checkRecordsCountIsSane() {
74+
if (recordsCount.get() < 0) {
75+
throw new RuntimeException("Disposed more records than were added");
76+
}
77+
}
78+
79+
private void addRecordsToDisposer(int count) {
80+
checkRecordsCountIsSane();
81+
82+
recordsCount.addAndGet(count);
83+
84+
MyDisposerRecord disposerRecord = new MyDisposerRecord();
85+
for (int i = 0; i < count; i++) {
86+
Disposer.addObjectRecord(new Object(), disposerRecord);
87+
}
88+
}
89+
90+
class MyDisposerRecord implements DisposerRecord {
91+
public void dispose() {
92+
recordsCount.decrementAndGet();
93+
}
94+
}
95+
96+
class FinalDisposerRecord implements DisposerRecord {
97+
public void dispose() {
98+
disposerDone = true;
99+
}
100+
}
101+
102+
private static void giveGCAChance() {
103+
try {
104+
Thread.sleep(2000);
105+
} catch (InterruptedException ignored) {}
106+
}
107+
108+
private static void generateOOME() throws Exception {
109+
final List<Object> leak = new LinkedList<>();
110+
try {
111+
while (true) {
112+
leak.add(new byte[1024 * 1024]);
113+
}
114+
} catch (OutOfMemoryError ignored) {}
115+
giveGCAChance();
116+
}
117+
}

0 commit comments

Comments
 (0)