📄 posixprocess.java
字号:
// PosixProcess.java - Subclass of Process for POSIX systems./* Copyright (C) 1998, 1999, 2004 Free Software Foundation This file is part of libgcj.This software is copyrighted work licensed under the terms of theLibgcj License. Please consult the file "LIBGCJ_LICENSE" fordetails. */package java.lang;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.HashMap;import java.util.LinkedList;import java.util.List;import java.util.Map;/** * @author Tom Tromey <tromey@cygnus.com> * @date May 3, 1999 * @author David Daney <ddaney@avtrex.com> Rewrote using * ProcessManager */// This is entirely internal to our implementation.// This file is copied to `ConcreteProcess.java' before compilation.// Hence the class name apparently does not match the file name.final class ConcreteProcess extends Process{ static class ProcessManager extends Thread { /** * A list of {@link ConcreteProcess ConcreteProcesses} to be * started. The queueLock object is used as the lock Object * for all process related operations. To avoid dead lock * ensure queueLock is obtained before ConcreteProcess. */ List queue = new LinkedList(); private Map pidToProcess = new HashMap(); private boolean ready = false; private long reaperPID; ProcessManager() { super("ProcessManager"); // Don't keep the (main) process from exiting on our account. this.setDaemon(true); } /** * Get the ConcreteProcess object with the given pid and * remove it from the map. This method is called from the * native code for {@link #reap()). The mapping is removed so * the ConcreteProcesses can be GCed after they terminate. * * @param p The pid of the process. */ private ConcreteProcess removeProcessFromMap(long p) { return (ConcreteProcess) pidToProcess.remove(new Long(p)); } /** * Put the given ConcreteProcess in the map using the Long * value of its pid as the key. * * @param p The ConcreteProcess. */ void addProcessToMap(ConcreteProcess p) { pidToProcess.put(new Long(p.pid), p); } /** * Queue up the ConcreteProcess and awake the ProcessManager. * The ProcessManager will start the ConcreteProcess from its * thread so it can be reaped when it terminates. * * @param p The ConcreteProcess. */ void startExecuting(ConcreteProcess p) { synchronized (queueLock) { queue.add(p); signalReaper(); // If blocked in waitForSignal(). queueLock.notifyAll(); // If blocked in wait(); } } /** * Block until the ProcessManager thread is ready to accept * commands. */ void waitUntilReady() { synchronized (this) { try { while (! ready) wait(); } catch (InterruptedException ie) { // Ignore. } } } /** * Main Process starting/reaping loop. */ public void run() { init(); // Now ready to accept requests. synchronized (this) { ready = true; this.notifyAll(); } for (;;) { try { synchronized (queueLock) { boolean haveMoreChildren = reap(); if (! haveMoreChildren && queue.size() == 0) { // This reaper thread could exit, but we // keep it alive for a while in case // someone wants to start more Processes. try { queueLock.wait(1000L); if (queue.size() == 0) { processManager = null; return; // Timed out. } } catch (InterruptedException ie) { // Ignore and exit the thread. return; } } while (queue.size() > 0) { ConcreteProcess p = (ConcreteProcess) queue.remove(0); p.spawn(this); } } // Wait for a SIGCHLD from either an exiting // process or the startExecuting() method. This // is done outside of the synchronized block to // allow other threads to enter and submit more // jobs. waitForSignal(); } catch (Exception ex) { ex.printStackTrace(System.err); } } } /** * Setup native signal handlers and other housekeeping things. * */ private native void init(); /** * Block waiting for SIGCHLD. * */ private native void waitForSignal(); /** * Try to reap as many children as possible without blocking. * * @return true if more live children exist. * */ private native boolean reap(); /** * Send SIGCHLD to the reaper thread. */ private native void signalReaper(); } public void destroy() { // Synchronized on the queueLock. This ensures that the reaper // thread cannot be doing a wait() on the child. // Otherwise there would be a race where the OS could // create a process with the same pid between the wait() // and the update of the state which would cause a kill to // the wrong process. synchronized (queueLock) { synchronized (this) { // If there is no ProcessManager we cannot kill. if (state != STATE_TERMINATED) { if (processManager == null) throw new InternalError(); nativeDestroy(); } } } } private native void nativeDestroy(); public int exitValue() { synchronized (this) { if (state != STATE_TERMINATED) throw new IllegalThreadStateException("Process has not exited"); } return status; } /** * Called by native code when process exits. * * Already synchronized (this). Close any streams that we can to * conserve file descriptors. * * The outputStream can be closed as any future writes will * generate an IOException due to EPIPE. * * The inputStream and errorStream can only be closed if the user * has not obtained a reference to them AND they have no bytes * available. Since the process has terminated they will never have * any more data available and can safely be replaced by * EOFInputStreams. */ void processTerminationCleanup() { try { outputStream.close(); } catch (IOException ioe) { // Ignore. } try { if (returnedErrorStream == null && errorStream.available() == 0) { errorStream.close(); errorStream = null; } } catch (IOException ioe) { // Ignore. } try { if (returnedInputStream == null && inputStream.available() == 0) { inputStream.close(); inputStream = null; } } catch (IOException ioe) { // Ignore. } } public synchronized InputStream getErrorStream() { if (returnedErrorStream != null) return returnedErrorStream; if (errorStream == null) returnedErrorStream = EOFInputStream.instance; else returnedErrorStream = errorStream; return returnedErrorStream; } public synchronized InputStream getInputStream() { if (returnedInputStream != null) return returnedInputStream; if (inputStream == null) returnedInputStream = EOFInputStream.instance; else returnedInputStream = inputStream; return returnedInputStream; } public OutputStream getOutputStream() { return outputStream; } public int waitFor() throws InterruptedException { synchronized (this) { while (state != STATE_TERMINATED) wait(); } return status; } /** * Start this process running. This should only be called by the * ProcessManager. * * @param pm The ProcessManager that made the call. */ void spawn(ProcessManager pm) { synchronized (this) { // Do the fork/exec magic. nativeSpawn(); // There is no race with reap() in the pidToProcess map // because this is always called from the same thread // doing the reaping. pm.addProcessToMap(this); state = STATE_RUNNING; // Notify anybody waiting on state change. this.notifyAll(); } } /** * Do the fork and exec. */ private native void nativeSpawn(); // This file is copied to `ConcreteProcess.java' before // compilation. Hence the constructor name apparently does not // match the file name. ConcreteProcess(String[] progarray, String[] envp, File dir) throws IOException { // Check to ensure there is something to run, and avoid // dereferencing null pointers in native code. if (progarray[0] == null) throw new NullPointerException(); this.progarray = progarray; this.envp = envp; this.dir = dir; // Start a ProcessManager if there is not one already running. synchronized (queueLock) { if (processManager == null) { processManager = new ProcessManager(); processManager.start(); processManager.waitUntilReady(); } // Queue this ConcreteProcess for starting by the ProcessManager. processManager.startExecuting(this); } // Wait until ProcessManager has started us. synchronized (this) { while (state == STATE_WAITING_TO_START) { try { wait(); } catch (InterruptedException ie) { // FIXME: What to do when interrupted while blocking in a constructor? // Ignore. } } } // If there was a problem, re-throw it. if (exception != null) { if (exception instanceof IOException) { IOException ioe = new IOException(exception.toString()); ioe.initCause(exception); throw ioe; } // Not an IOException. Something bad happened. InternalError ie = new InternalError(exception.toString()); ie.initCause(exception); throw ie; } // If we get here, all is well, the Process has started. } private String[] progarray; private String[] envp; private File dir; /** Set by the ProcessManager on problems starting. */ private Throwable exception; /** The process id. This is cast to a pid_t on the native side. */ private long pid; // FIXME: Why doesn't the friend declaration in ConcreteProcess.h // allow ConcreteProcess$ProcessManager native code access these // when they are private? /** Before the process is forked. */ static final int STATE_WAITING_TO_START = 0; /** After the fork. */ static final int STATE_RUNNING = 1; /** After exit code has been collected. */ static final int STATE_TERMINATED = 2; /** One of STATE_WAITING_TO_START, STATE_RUNNING, STATE_TERMINATED. */ int state; /** The exit status, if the child has exited. */ int status; /** The streams. */ private InputStream errorStream; private InputStream inputStream; private OutputStream outputStream; /** InputStreams obtained by the user. Not null indicates that the * user has obtained the stream. */ private InputStream returnedErrorStream; private InputStream returnedInputStream; /** * Lock Object for all processManager related locking. */ private static Object queueLock = new Object(); private static ProcessManager processManager; static class EOFInputStream extends InputStream { static EOFInputStream instance = new EOFInputStream(); public int read() { return -1; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -