[1775996 views]

[]

Odi's astoundingly incomplete notes

New entries | Code

JDK bug in DocPrintJob.print

The problem in the Java 1.5 printing API mentioned earlier seems to be even bigger. If documents are printed concurrently to different printers, the API may make concurrent calls to OpenSSL (which is not thread-safe). That in turn causes memory corruption and leads to VM crashes. The crashes may occur immediately on malloc in the OpenSSL code or afterwards in arbitrary places (even interpreted Java code). The only workaround is to synchronize all printing API calls to a global lock. Bug report at Sun.

Stack of crashing thread:
Stack: [0x6c377000,0x6c3f8000),  sp=0x6c3f6f74,  free space=511k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libc.so.6+0x71cee]
C  [libc.so.6+0x710fd]  malloc+0x8d
C  [libcrypto.so.4+0x2b500]
C  [libcrypto.so.4+0x2bb5c]  CRYPTO_malloc+0x4c
C  [libcrypto.so.4+0x7883e]  OBJ_NAME_add+0x5e
C  [libcrypto.so.4+0x7d2ee]  EVP_add_cipher+0x6e
C  [libssl.so.4+0x29283]  SSL_library_init+0x33
C  [libcups.so.2+0x6003]  httpInitialize+0x63
C  [libcups.so.2+0x62d8]  httpConnectEncrypt+0x28
C  [libcups.so.2+0x62a8]  httpConnect+0x38
C  [libmawt.so+0x400f]  Java_sun_print_CUPSPrinter_canConnect+0x4f
j  sun.print.CUPSPrinter.canConnect(Ljava/lang/String;I)Z+0
j  sun.print.CUPSPrinter.isCupsRunning()Z+71
j  sun.print.UnixPrintJob.print(Ljavax/print/Doc;Ljavax/print/attribute/PrintRequestAttributeSet;)V+186

Test code that reproduces the problem:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;

import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaSizeName;

/**
* Crashes on Linux when printin a PDF to two different printers.
*
* @author Ortwin Glück
*/
public class Test implements Runnable{
private static AtomicInteger select = new AtomicInteger();
private PrintService service1, service2;
private Object latch = new Object();
private String file;

public static void main(String[] args) {
if (args.length != 3) {
System.out.println("Syntax: Test printer printer file");
return;
}
Test app = new Test(args[0], args[1], args[2]);
app.run();
}

public Test(String printer1, String printer2, String file) {
PrintService[] services = PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.AUTOSENSE, null);
for (PrintService ps : services) {
if (printer1.equals(ps.getName())) {
service1 = ps;
}
if (printer2.equals(ps.getName())) {
service2 = ps;
}
}
if (service1 == null)
throw new IllegalArgumentException("Unknown printer "+ printer1);
if (service2 == null)
throw new IllegalArgumentException("Unknown printer "+ printer2);
File f = new File(file);
if (!f.exists())
throw new IllegalArgumentException("File not found: "+ f.getAbsolutePath());
if (!f.isFile())
throw new IllegalArgumentException("Not a file: "+ f.getAbsolutePath());
this.file = file;
}

public void run() {
int THREADS = 20;
for (int i=0; i<THREADS; i++) {
Thread t = new Thread(new Job());
t.setDaemon(false);
t.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}

synchronized (latch) {
latch.notifyAll();
}

}

private class Job implements Runnable {
public void run() {
synchronized (latch) {
try {
latch.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
InputStream stream;
try {
stream = new FileInputStream(file);
} catch(FileNotFoundException e) {
// does not happen
throw new RuntimeException(e.getMessage(), e);
}

DocPrintJob dpj;
if (select.getAndIncrement() % 2 == 0) {
dpj = service1.createPrintJob();
} else {
dpj = service2.createPrintJob();
}
DocAttributeSet docaset = new HashDocAttributeSet();
docaset.add(MediaSizeName.ISO_A4);
SimpleDoc simpleDoc = new SimpleDoc(stream, DocFlavor.INPUT_STREAM.AUTOSENSE, docaset);
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
try {
//synchronized (Test.this) {
dpj.print(simpleDoc, aset);
//}
} catch(Exception e) {
System.err.print(e.getMessage());
}
}

}
}



posted on 2007-08-28 11:24 UTC in Code | 0 comments | permalink