A DESCRIPTION OF THE REQUEST :
The Sun Linux JDK class java.lang.UNIXProcess creates a new thread each time a process starts. This slows down things if you need to call external processes frequently.
JUSTIFICATION :
For performance reasons.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
java.lang.UNIXProcess should use thread pooling.
ACTUAL -
A new thread is created each time a process starts.
CUSTOMER SUBMITTED WORKAROUND :
Put the following patched version of UNIXProcess onto the boot classpath:
/*
* @(#)UNIXProcess.java.linux 1.37 04/01/12
*
* Copyright 1995-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*/
package java.lang;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
/**
* java.lang.Process subclass in the UNIX environment.
* This is an unofficial patched version of the same named class of Sun JDK 1.5.
* <p>
* Unlike the original this one uses thread pooling. Additionally it avoids overhead of
* {@link AccessController} if no {@link SecurityManager} is active.
* </p>
*
* @author Mario Wolczko and Ross Knippel.
* @author Konstantin Kladko (ported to Linux)
* @author <a href="mailto:###@###.###">Jörg Waßmer</a> (thread pooling, unofficial patch)
*/
final class UNIXProcess extends Process
{
private static final ExecutorService threadFactory = Executors.newCachedThreadPool();
private final FileDescriptor stdin_fd;
private final FileDescriptor stdout_fd;
private final FileDescriptor stderr_fd;
private int pid;
private int exitcode;
private boolean hasExited;
private BufferedOutputStream stdin_stream;
private BufferedInputStream stdout_stream;
private FileInputStream stderr_stream;
private static native void destroyProcess(int pid);
private native int forkAndExec(byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
boolean redirectErrorStream,
FileDescriptor stdin_fd,
FileDescriptor stdout_fd,
FileDescriptor stderr_fd)
throws IOException;
/**
* This is for the reaping thread
*/
private native int waitForProcessExit(int pid);
/* In the process constructor we wait on this gate until the process */
/* has been created. Then we return from the constructor. */
/* fork() is called by the same thread which later waits for the process */
/* to terminate */
UNIXProcess(
byte[] prog,
byte[] argBlock, final int argc,
byte[] envBlock, final int envc,
byte[] dir, final boolean redirectErrorStream
)
throws IOException
{
super();
this.stdin_fd = new FileDescriptor();
this.stdout_fd = new FileDescriptor();
this.stderr_fd = new FileDescriptor();
final UNIXProcess.Gate gate = new UNIXProcess.Gate();
/*
* For each subprocess forked a corresponding reaper thread
* is started. That thread is the only thread which waits
* for the subprocess to terminate and it doesn't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
if (System.getSecurityManager() == null)
{
// avoid overhead of AccessController
UNIXProcess.threadFactory.execute(new ProcessReaper(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
}
else
{
// extra method to avoid classloading
startProcessReaperPrivileged(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
);
}
// gc
prog = null;
argBlock = null;
envBlock = null;
dir = null;
gate.waitForExit();
IOException ex = gate.getException();
if (ex != null)
{
throw (IOException) new IOException("unable to create process").initCause(ex);
}
}
private void startProcessReaperPrivileged(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
AccessController.doPrivileged(new StartProcessReaperAction(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
}
public OutputStream getOutputStream()
{
return this.stdin_stream;
}
public InputStream getInputStream()
{
return this.stdout_stream;
}
public InputStream getErrorStream()
{
return this.stderr_stream;
}
public synchronized int waitFor() throws InterruptedException
{
while (!this.hasExited)
{
wait();
}
return this.exitcode;
}
public synchronized int exitValue()
{
if (!this.hasExited)
{
throw new IllegalThreadStateException("process hasn't exited");
}
return this.exitcode;
}
public void destroy()
{
UNIXProcess.destroyProcess(this.pid);
try
{
this.stdin_stream.close();
}
catch (IOException e)
{
// ignore
}
try
{
this.stdout_stream.close();
}
catch (IOException e)
{
// ignore
}
try
{
this.stderr_stream.close();
}
catch (IOException e)
{
// ignore
}
}
private static final class Gate extends Object
{
private boolean exited = false;
private IOException savedException;
Gate()
{
super();
}
/**
* Opens the gate.
*/
synchronized void exit()
{
/* Opens the gate */
exited = true;
this.notify();
}
/**
* Wait until the gate is open.
*/
synchronized void waitForExit()
{
boolean interrupted = false;
while (!exited)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
interrupted = true;
}
}
if (interrupted)
{
Thread.currentThread().interrupt();
}
}
void setException(IOException e)
{
savedException = e;
}
IOException getException()
{
return savedException;
}
}
private final class ProcessReaper extends Object implements Runnable
{
private UNIXProcess.Gate gate;
private byte[] prog;
private byte[] argBlock;
private final int argc;
private byte[] envBlock;
private final int envc;
private byte[] dir;
private final boolean redirectErrorStream;
ProcessReaper(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
super();
this.gate = gate;
this.prog = prog;
this.argBlock = argBlock;
this.argc = argc;
this.envBlock = envBlock;
this.envc = envc;
this.dir = dir;
this.redirectErrorStream = redirectErrorStream;
}
public void run()
{
try
{
UNIXProcess.this.pid = UNIXProcess.this.forkAndExec(
this.prog,
this.argBlock, this.argc,
this.envBlock, this.envc,
this.dir, this.redirectErrorStream,
UNIXProcess.this.stdin_fd, UNIXProcess.this.stdout_fd, UNIXProcess.this.stderr_fd
);
}
catch (IOException e)
{
this.gate.setException(e); // rethrown by UNIXProcess constructor
this.gate.exit();
return;
}
finally
{
// gc
this.prog = null;
this.argBlock = null;
this.envBlock = null;
this.dir = null;
}
if (System.getSecurityManager() == null)
createStreams(); // avoid overhead of AccessController
else
createStreamsPrivileged(); // extra method to avoid classloading
this.gate.exit(); // causes exit from UNIXProcess constructor
this.gate = null;
int res = UNIXProcess.this.waitForProcessExit(pid);
synchronized (UNIXProcess.this)
{
UNIXProcess.this.hasExited = true;
UNIXProcess.this.exitcode = res;
UNIXProcess.this.notifyAll();
}
}
private void createStreams()
{
UNIXProcess.this.stdin_stream = new BufferedOutputStream(
new FileOutputStream(UNIXProcess.this.stdin_fd)
);
UNIXProcess.this.stdout_stream = new BufferedInputStream(
new FileInputStream(UNIXProcess.this.stdout_fd)
);
UNIXProcess.this.stderr_stream = new FileInputStream(UNIXProcess.this.stderr_fd);
}
private void createStreamsPrivileged()
{
AccessController.doPrivileged(new ProcessReaper.CreateStreamsAction());
}
private final class CreateStreamsAction extends Object implements PrivilegedAction
{
public Object run()
{
ProcessReaper.this.createStreams();
return null;
}
}
}
private final class StartProcessReaperAction extends Object implements PrivilegedAction
{
private final UNIXProcess.Gate gate;
private final byte[] prog;
private final byte[] argBlock;
private final int argc;
private final byte[] envBlock;
private final int envc;
private final byte[] dir;
private final boolean redirectErrorStream;
StartProcessReaperAction(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
super();
this.gate = gate;
this.prog = prog;
this.argBlock = argBlock;
this.argc = argc;
this.envBlock = envBlock;
this.envc = envc;
this.dir = dir;
this.redirectErrorStream = redirectErrorStream;
}
public Object run()
{
UNIXProcess.threadFactory.execute(new ProcessReaper(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
return null;
}
}
}
###@###.### 2004-12-16 17:22:00 GMT
The Sun Linux JDK class java.lang.UNIXProcess creates a new thread each time a process starts. This slows down things if you need to call external processes frequently.
JUSTIFICATION :
For performance reasons.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
java.lang.UNIXProcess should use thread pooling.
ACTUAL -
A new thread is created each time a process starts.
CUSTOMER SUBMITTED WORKAROUND :
Put the following patched version of UNIXProcess onto the boot classpath:
/*
* @(#)UNIXProcess.java.linux 1.37 04/01/12
*
* Copyright 1995-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*/
package java.lang;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
/**
* java.lang.Process subclass in the UNIX environment.
* This is an unofficial patched version of the same named class of Sun JDK 1.5.
* <p>
* Unlike the original this one uses thread pooling. Additionally it avoids overhead of
* {@link AccessController} if no {@link SecurityManager} is active.
* </p>
*
* @author Mario Wolczko and Ross Knippel.
* @author Konstantin Kladko (ported to Linux)
* @author <a href="mailto:###@###.###">Jörg Waßmer</a> (thread pooling, unofficial patch)
*/
final class UNIXProcess extends Process
{
private static final ExecutorService threadFactory = Executors.newCachedThreadPool();
private final FileDescriptor stdin_fd;
private final FileDescriptor stdout_fd;
private final FileDescriptor stderr_fd;
private int pid;
private int exitcode;
private boolean hasExited;
private BufferedOutputStream stdin_stream;
private BufferedInputStream stdout_stream;
private FileInputStream stderr_stream;
private static native void destroyProcess(int pid);
private native int forkAndExec(byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
boolean redirectErrorStream,
FileDescriptor stdin_fd,
FileDescriptor stdout_fd,
FileDescriptor stderr_fd)
throws IOException;
/**
* This is for the reaping thread
*/
private native int waitForProcessExit(int pid);
/* In the process constructor we wait on this gate until the process */
/* has been created. Then we return from the constructor. */
/* fork() is called by the same thread which later waits for the process */
/* to terminate */
UNIXProcess(
byte[] prog,
byte[] argBlock, final int argc,
byte[] envBlock, final int envc,
byte[] dir, final boolean redirectErrorStream
)
throws IOException
{
super();
this.stdin_fd = new FileDescriptor();
this.stdout_fd = new FileDescriptor();
this.stderr_fd = new FileDescriptor();
final UNIXProcess.Gate gate = new UNIXProcess.Gate();
/*
* For each subprocess forked a corresponding reaper thread
* is started. That thread is the only thread which waits
* for the subprocess to terminate and it doesn't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
if (System.getSecurityManager() == null)
{
// avoid overhead of AccessController
UNIXProcess.threadFactory.execute(new ProcessReaper(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
}
else
{
// extra method to avoid classloading
startProcessReaperPrivileged(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
);
}
// gc
prog = null;
argBlock = null;
envBlock = null;
dir = null;
gate.waitForExit();
IOException ex = gate.getException();
if (ex != null)
{
throw (IOException) new IOException("unable to create process").initCause(ex);
}
}
private void startProcessReaperPrivileged(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
AccessController.doPrivileged(new StartProcessReaperAction(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
}
public OutputStream getOutputStream()
{
return this.stdin_stream;
}
public InputStream getInputStream()
{
return this.stdout_stream;
}
public InputStream getErrorStream()
{
return this.stderr_stream;
}
public synchronized int waitFor() throws InterruptedException
{
while (!this.hasExited)
{
wait();
}
return this.exitcode;
}
public synchronized int exitValue()
{
if (!this.hasExited)
{
throw new IllegalThreadStateException("process hasn't exited");
}
return this.exitcode;
}
public void destroy()
{
UNIXProcess.destroyProcess(this.pid);
try
{
this.stdin_stream.close();
}
catch (IOException e)
{
// ignore
}
try
{
this.stdout_stream.close();
}
catch (IOException e)
{
// ignore
}
try
{
this.stderr_stream.close();
}
catch (IOException e)
{
// ignore
}
}
private static final class Gate extends Object
{
private boolean exited = false;
private IOException savedException;
Gate()
{
super();
}
/**
* Opens the gate.
*/
synchronized void exit()
{
/* Opens the gate */
exited = true;
this.notify();
}
/**
* Wait until the gate is open.
*/
synchronized void waitForExit()
{
boolean interrupted = false;
while (!exited)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
interrupted = true;
}
}
if (interrupted)
{
Thread.currentThread().interrupt();
}
}
void setException(IOException e)
{
savedException = e;
}
IOException getException()
{
return savedException;
}
}
private final class ProcessReaper extends Object implements Runnable
{
private UNIXProcess.Gate gate;
private byte[] prog;
private byte[] argBlock;
private final int argc;
private byte[] envBlock;
private final int envc;
private byte[] dir;
private final boolean redirectErrorStream;
ProcessReaper(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
super();
this.gate = gate;
this.prog = prog;
this.argBlock = argBlock;
this.argc = argc;
this.envBlock = envBlock;
this.envc = envc;
this.dir = dir;
this.redirectErrorStream = redirectErrorStream;
}
public void run()
{
try
{
UNIXProcess.this.pid = UNIXProcess.this.forkAndExec(
this.prog,
this.argBlock, this.argc,
this.envBlock, this.envc,
this.dir, this.redirectErrorStream,
UNIXProcess.this.stdin_fd, UNIXProcess.this.stdout_fd, UNIXProcess.this.stderr_fd
);
}
catch (IOException e)
{
this.gate.setException(e); // rethrown by UNIXProcess constructor
this.gate.exit();
return;
}
finally
{
// gc
this.prog = null;
this.argBlock = null;
this.envBlock = null;
this.dir = null;
}
if (System.getSecurityManager() == null)
createStreams(); // avoid overhead of AccessController
else
createStreamsPrivileged(); // extra method to avoid classloading
this.gate.exit(); // causes exit from UNIXProcess constructor
this.gate = null;
int res = UNIXProcess.this.waitForProcessExit(pid);
synchronized (UNIXProcess.this)
{
UNIXProcess.this.hasExited = true;
UNIXProcess.this.exitcode = res;
UNIXProcess.this.notifyAll();
}
}
private void createStreams()
{
UNIXProcess.this.stdin_stream = new BufferedOutputStream(
new FileOutputStream(UNIXProcess.this.stdin_fd)
);
UNIXProcess.this.stdout_stream = new BufferedInputStream(
new FileInputStream(UNIXProcess.this.stdout_fd)
);
UNIXProcess.this.stderr_stream = new FileInputStream(UNIXProcess.this.stderr_fd);
}
private void createStreamsPrivileged()
{
AccessController.doPrivileged(new ProcessReaper.CreateStreamsAction());
}
private final class CreateStreamsAction extends Object implements PrivilegedAction
{
public Object run()
{
ProcessReaper.this.createStreams();
return null;
}
}
}
private final class StartProcessReaperAction extends Object implements PrivilegedAction
{
private final UNIXProcess.Gate gate;
private final byte[] prog;
private final byte[] argBlock;
private final int argc;
private final byte[] envBlock;
private final int envc;
private final byte[] dir;
private final boolean redirectErrorStream;
StartProcessReaperAction(
Gate gate,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir, boolean redirectErrorStream
)
{
super();
this.gate = gate;
this.prog = prog;
this.argBlock = argBlock;
this.argc = argc;
this.envBlock = envBlock;
this.envc = envc;
this.dir = dir;
this.redirectErrorStream = redirectErrorStream;
}
public Object run()
{
UNIXProcess.threadFactory.execute(new ProcessReaper(
gate,
prog,
argBlock, argc,
envBlock, envc,
dir, redirectErrorStream
));
return null;
}
}
}
###@###.### 2004-12-16 17:22:00 GMT