Skip to content

Commit 00decca

Browse files
Maxim KartashevAlexey Ushakov
authored andcommitted
8289208: Test DrawRotatedStringUsingRotatedFont.java occasionally crashes on MacOS
Reviewed-by: prr, avu
1 parent 9f8cc42 commit 00decca

File tree

3 files changed

+136
-21
lines changed

3 files changed

+136
-21
lines changed

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

Lines changed: 19 additions & 20 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,12 +145,12 @@ 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();
152152
} catch (Exception e) {
153-
System.out.println("Exception while removing reference.");
153+
e.printStackTrace(System.err);
154154
}
155155
}
156156
}
@@ -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+
e.printStackTrace(System.err);
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,22 +213,19 @@ 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) {
229-
System.out.println("Exception while removing reference.");
228+
e.printStackTrace(System.err);
230229
} finally {
231230
pollingQueue = false;
232231
}

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

0 commit comments

Comments
 (0)