Skip to content

Commit 6875678

Browse files
committed
8273831: PrintServiceLookup spawns 2 threads in the current classloader, getting orphaned
Reviewed-by: aivanov
1 parent 5bbc8d3 commit 6875678

File tree

3 files changed

+117
-16
lines changed

3 files changed

+117
-16
lines changed

src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
package sun.print;
2727

2828
import java.io.BufferedReader;
29-
import java.io.FileInputStream;
30-
import java.io.InputStream;
31-
import java.io.InputStreamReader;
3229
import java.io.IOException;
30+
import java.security.PrivilegedAction;
3331
import java.util.ArrayList;
3432
import java.util.Vector;
3533
import java.security.AccessController;
@@ -54,6 +52,8 @@
5452
import java.net.URL;
5553
import java.nio.file.Files;
5654

55+
import sun.awt.util.ThreadGroupUtils;
56+
5757
/*
5858
* Remind: This class uses solaris commands. We also need a linux
5959
* version
@@ -204,14 +204,18 @@ static int getBSDCommandIndex() {
204204
return BSD_LPD;
205205
}
206206

207-
207+
@SuppressWarnings("removal")
208208
public PrintServiceLookupProvider() {
209209
// start the printer listener thread
210210
if (pollServices) {
211-
Thread thr = new Thread(null, new PrinterChangeListener(),
212-
"PrinterListener", 0, false);
213-
thr.setDaemon(true);
214-
thr.start();
211+
AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
212+
Thread thr = new Thread(ThreadGroupUtils.getRootThreadGroup(),
213+
new PrinterChangeListener(),
214+
"PrinterListener", 0, false);
215+
thr.setContextClassLoader(null);
216+
thr.setDaemon(true);
217+
return thr;
218+
}).start();
215219
IPPPrintService.debug_println(debugPrefix+"polling turned on");
216220
}
217221
}

src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
package sun.print;
2727

28+
import java.security.AccessController;
29+
import java.security.PrivilegedAction;
2830
import java.util.ArrayList;
2931

3032
import javax.print.DocFlavor;
@@ -41,6 +43,8 @@
4143
import javax.print.attribute.PrintServiceAttributeSet;
4244
import javax.print.attribute.standard.PrinterName;
4345

46+
import sun.awt.util.ThreadGroupUtils;
47+
4448
public class PrintServiceLookupProvider extends PrintServiceLookup {
4549

4650
private PrintService defaultPrintService;
@@ -81,22 +85,31 @@ public static PrintServiceLookupProvider getWin32PrintLUS() {
8185
return win32PrintLUS;
8286
}
8387

88+
@SuppressWarnings("removal")
8489
public PrintServiceLookupProvider() {
8590

8691
if (win32PrintLUS == null) {
8792
win32PrintLUS = this;
8893

8994
// start the local printer listener thread
90-
Thread thr = new Thread(null, new PrinterChangeListener(),
91-
"PrinterListener", 0, false);
92-
thr.setDaemon(true);
93-
thr.start();
95+
AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
96+
Thread thr = new Thread(ThreadGroupUtils.getRootThreadGroup(),
97+
new PrinterChangeListener(),
98+
"PrinterListener", 0, false);
99+
thr.setContextClassLoader(null);
100+
thr.setDaemon(true);
101+
return thr;
102+
}).start();
94103

95104
// start the remote printer listener thread
96-
Thread remThr = new Thread(null, new RemotePrinterChangeListener(),
97-
"RemotePrinterListener", 0, false);
98-
remThr.setDaemon(true);
99-
remThr.start();
105+
AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
106+
Thread thr = new Thread(ThreadGroupUtils.getRootThreadGroup(),
107+
new RemotePrinterChangeListener(),
108+
"RemotePrinterListener", 0, false);
109+
thr.setContextClassLoader(null);
110+
thr.setDaemon(true);
111+
return thr;
112+
}).start();
100113
} /* else condition ought to never happen! */
101114
}
102115

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. 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+
24+
import java.awt.EventQueue;
25+
import java.lang.ref.Reference;
26+
import java.lang.ref.WeakReference;
27+
import java.net.URL;
28+
import java.net.URLClassLoader;
29+
30+
import javax.print.DocFlavor;
31+
import javax.print.PrintServiceLookup;
32+
33+
/**
34+
* @test
35+
* @bug 8273831
36+
* @summary Tests custom class loader cleanup
37+
*/
38+
public final class FlushCustomClassLoader {
39+
40+
public static void main(String[] args) throws Exception {
41+
Reference<ClassLoader> loader = getLoader("testMethod");
42+
43+
int attempt = 0;
44+
while (loader.get() != null) {
45+
if (++attempt > 10) {
46+
throw new RuntimeException("Too many attempts: " + attempt);
47+
}
48+
System.gc();
49+
Thread.sleep(1000);
50+
System.out.println("Not freed, attempt: " + attempt);
51+
}
52+
}
53+
54+
public static void testMethod() {
55+
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
56+
PrintServiceLookup.lookupPrintServices(flavor, null);
57+
}
58+
59+
private static Reference<ClassLoader> getLoader(String m) throws Exception {
60+
/*
61+
* The print services are stored per the AppContext, and each AppContext
62+
* caches the "current" class loader during creation.
63+
* see javax.print.PrintServiceLookup.
64+
*
65+
* To prevent AppContext from cache our test loader we force AppContext
66+
* creation early by the invokeAndWait.
67+
* The "EventQueue.invokeAndWait(() -> {});" can be removed when the
68+
* AppContext usage will be deleted in the PrintServiceLookup
69+
*/
70+
EventQueue.invokeAndWait(() -> {});
71+
72+
URL url = FlushCustomClassLoader.class.getProtectionDomain()
73+
.getCodeSource().getLocation();
74+
URLClassLoader loader = new URLClassLoader(new URL[]{url}, null);
75+
76+
Thread ct = Thread.currentThread();
77+
ct.setContextClassLoader(loader);
78+
Class<?> cls = Class.forName("FlushCustomClassLoader", true, loader);
79+
cls.getDeclaredMethod(m).invoke(null);
80+
ct.setContextClassLoader(null);
81+
loader.close();
82+
return new WeakReference<>(loader);
83+
}
84+
}

0 commit comments

Comments
 (0)