changeset 10586:d88c2b548255 tip

Automated merge with http://hg.openjdk.java.net/jdk9/dev/jdk
author wmdietl
date Wed, 23 Jul 2014 15:00:47 -0400
parents 3b298c230549 ef7f5e5f35a6
children
files .hgtags make/GenerateData.gmk src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java src/share/classes/com/sun/java/swing/plaf/gtk/GTKFileChooserUI.java src/share/classes/com/sun/jmx/snmp/agent/SnmpMibGroup.java src/share/classes/com/sun/tools/example/debug/expr/TokenMgrError.java src/share/classes/java/awt/AWTEventMulticaster.java src/share/classes/java/awt/AWTKeyStroke.java src/share/classes/java/awt/CardLayout.java src/share/classes/java/awt/Dialog.java src/share/classes/java/awt/Graphics2D.java src/share/classes/java/awt/KeyboardFocusManager.java src/share/classes/java/awt/MediaTracker.java src/share/classes/java/awt/ScrollPaneAdjustable.java src/share/classes/java/awt/event/InputEvent.java src/share/classes/java/awt/font/TextAttribute.java src/share/classes/java/lang/Class.java src/share/classes/java/lang/ConditionalSpecialCasing.java src/share/classes/java/lang/Integer.java src/share/classes/java/lang/Long.java src/share/classes/java/lang/SecurityManager.java src/share/classes/java/time/Instant.java src/share/classes/java/time/OffsetDateTime.java src/share/classes/java/time/ZonedDateTime.java src/share/classes/java/util/Collections.java src/share/classes/java/util/Date.java src/share/classes/java/util/ListResourceBundle.java src/share/classes/java/util/ResourceBundle.java src/share/classes/java/util/logging/FileHandler.java src/share/classes/java/util/stream/DoubleStream.java src/share/classes/java/util/stream/IntStream.java src/share/classes/java/util/stream/LongStream.java src/share/classes/javax/print/DocFlavor.java src/share/classes/javax/print/ServiceUI.java src/share/classes/javax/print/attribute/HashAttributeSet.java src/share/classes/javax/print/attribute/standard/MediaSize.java src/share/classes/javax/sound/sampled/ReverbType.java src/share/classes/javax/sql/rowset/RowSetMetaDataImpl.java src/share/classes/javax/swing/AbstractAction.java src/share/classes/javax/swing/AbstractButton.java src/share/classes/javax/swing/AbstractListModel.java src/share/classes/javax/swing/CellRendererPane.java src/share/classes/javax/swing/DefaultBoundedRangeModel.java src/share/classes/javax/swing/DefaultButtonModel.java src/share/classes/javax/swing/DefaultCellEditor.java src/share/classes/javax/swing/DefaultListCellRenderer.java src/share/classes/javax/swing/DefaultListModel.java src/share/classes/javax/swing/DefaultListSelectionModel.java src/share/classes/javax/swing/DefaultSingleSelectionModel.java src/share/classes/javax/swing/ImageIcon.java src/share/classes/javax/swing/JApplet.java src/share/classes/javax/swing/JCheckBox.java src/share/classes/javax/swing/JCheckBoxMenuItem.java src/share/classes/javax/swing/JColorChooser.java src/share/classes/javax/swing/JComboBox.java src/share/classes/javax/swing/JDesktopPane.java src/share/classes/javax/swing/JDialog.java src/share/classes/javax/swing/JEditorPane.java src/share/classes/javax/swing/JFrame.java src/share/classes/javax/swing/JInternalFrame.java src/share/classes/javax/swing/JLabel.java src/share/classes/javax/swing/JLayeredPane.java src/share/classes/javax/swing/JList.java src/share/classes/javax/swing/JOptionPane.java src/share/classes/javax/swing/JPanel.java src/share/classes/javax/swing/JPasswordField.java src/share/classes/javax/swing/JProgressBar.java src/share/classes/javax/swing/JRadioButton.java src/share/classes/javax/swing/JRadioButtonMenuItem.java src/share/classes/javax/swing/JScrollBar.java src/share/classes/javax/swing/JScrollPane.java src/share/classes/javax/swing/JSlider.java src/share/classes/javax/swing/JSpinner.java src/share/classes/javax/swing/JSplitPane.java src/share/classes/javax/swing/JTabbedPane.java src/share/classes/javax/swing/JTable.java src/share/classes/javax/swing/JTextArea.java src/share/classes/javax/swing/JTextField.java src/share/classes/javax/swing/JTextPane.java src/share/classes/javax/swing/JToggleButton.java src/share/classes/javax/swing/JToolBar.java src/share/classes/javax/swing/JViewport.java src/share/classes/javax/swing/KeyStroke.java src/share/classes/javax/swing/MenuSelectionManager.java src/share/classes/javax/swing/OverlayLayout.java src/share/classes/javax/swing/ScrollPaneLayout.java src/share/classes/javax/swing/SizeRequirements.java src/share/classes/javax/swing/Spring.java src/share/classes/javax/swing/SpringLayout.java src/share/classes/javax/swing/SwingUtilities.java src/share/classes/javax/swing/UIDefaults.java src/share/classes/javax/swing/UIManager.java src/share/classes/javax/swing/UnsupportedLookAndFeelException.java src/share/classes/javax/swing/ViewportLayout.java src/share/classes/javax/swing/event/EventListenerList.java src/share/classes/javax/swing/event/HyperlinkEvent.java src/share/classes/javax/swing/event/ListSelectionEvent.java src/share/classes/javax/swing/event/TableColumnModelEvent.java src/share/classes/javax/swing/event/TableModelEvent.java src/share/classes/javax/swing/event/TreeExpansionEvent.java src/share/classes/javax/swing/event/TreeModelEvent.java src/share/classes/javax/swing/plaf/basic/BasicComboBoxEditor.java src/share/classes/javax/swing/plaf/basic/BasicComboBoxRenderer.java src/share/classes/javax/swing/plaf/basic/BasicComboPopup.java src/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java src/share/classes/javax/swing/plaf/basic/BasicListUI.java src/share/classes/javax/swing/plaf/basic/ComboPopup.java src/share/classes/javax/swing/plaf/metal/MetalButtonUI.java src/share/classes/javax/swing/plaf/metal/MetalCheckBoxIcon.java src/share/classes/javax/swing/plaf/metal/MetalCheckBoxUI.java src/share/classes/javax/swing/plaf/metal/MetalComboBoxButton.java src/share/classes/javax/swing/plaf/metal/MetalComboBoxEditor.java src/share/classes/javax/swing/plaf/metal/MetalComboBoxUI.java src/share/classes/javax/swing/plaf/metal/MetalIconFactory.java src/share/classes/javax/swing/plaf/metal/MetalProgressBarUI.java src/share/classes/javax/swing/plaf/metal/MetalRadioButtonUI.java src/share/classes/javax/swing/plaf/metal/MetalScrollButton.java src/share/classes/javax/swing/plaf/metal/MetalScrollPaneUI.java src/share/classes/javax/swing/plaf/metal/MetalSeparatorUI.java src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java src/share/classes/javax/swing/plaf/metal/MetalSplitPaneUI.java src/share/classes/javax/swing/plaf/metal/MetalTabbedPaneUI.java src/share/classes/javax/swing/plaf/metal/MetalTextFieldUI.java src/share/classes/javax/swing/plaf/metal/MetalToggleButtonUI.java src/share/classes/javax/swing/plaf/metal/MetalToolTipUI.java src/share/classes/javax/swing/plaf/multi/MultiLookAndFeel.java src/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java src/share/classes/javax/swing/plaf/nimbus/NimbusStyle.java src/share/classes/javax/swing/plaf/synth/SynthTextAreaUI.java src/share/classes/javax/swing/plaf/synth/SynthTextFieldUI.java src/share/classes/javax/swing/table/DefaultTableColumnModel.java src/share/classes/javax/swing/table/DefaultTableModel.java src/share/classes/javax/swing/table/JTableHeader.java src/share/classes/javax/swing/table/TableColumn.java src/share/classes/javax/swing/text/AbstractDocument.java src/share/classes/javax/swing/text/DateFormatter.java src/share/classes/javax/swing/text/DefaultFormatter.java src/share/classes/javax/swing/text/DefaultStyledDocument.java src/share/classes/javax/swing/text/InternationalFormatter.java src/share/classes/javax/swing/text/JTextComponent.java src/share/classes/javax/swing/text/NumberFormatter.java src/share/classes/javax/swing/text/StringContent.java src/share/classes/javax/swing/text/StyleContext.java src/share/classes/javax/swing/text/TextAction.java src/share/classes/javax/swing/text/html/HTMLDocument.java src/share/classes/javax/swing/text/html/HTMLEditorKit.java src/share/classes/javax/swing/text/html/Option.java src/share/classes/javax/swing/text/html/StyleSheet.java src/share/classes/javax/swing/text/rtf/RTFReader.java src/share/classes/javax/swing/tree/AbstractLayoutCache.java src/share/classes/javax/swing/tree/DefaultMutableTreeNode.java src/share/classes/javax/swing/tree/DefaultTreeCellEditor.java src/share/classes/javax/swing/tree/DefaultTreeModel.java src/share/classes/javax/swing/tree/DefaultTreeSelectionModel.java src/share/classes/javax/swing/tree/TreePath.java src/share/classes/javax/swing/tree/VariableHeightLayoutCache.java src/share/classes/org/jcp/xml/dsig/internal/dom/DOMPGPData.java src/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java src/share/classes/org/jcp/xml/dsig/internal/dom/DOMXMLSignature.java src/share/classes/sun/applet/AppletSecurity.java src/share/classes/sun/applet/AppletViewer.java src/share/classes/sun/awt/FontConfiguration.java src/share/classes/sun/awt/SunToolkit.java src/share/classes/sun/awt/datatransfer/ClipboardTransferable.java src/share/classes/sun/awt/image/ByteBandedRaster.java src/share/classes/sun/awt/image/ByteComponentRaster.java src/share/classes/sun/awt/image/IntegerComponentRaster.java src/share/classes/sun/awt/image/ShortBandedRaster.java src/share/classes/sun/awt/image/ShortComponentRaster.java src/share/classes/sun/font/ExtendedTextSourceLabel.java src/share/classes/sun/font/SunFontManager.java src/share/classes/sun/font/TrueTypeFont.java src/share/classes/sun/jvmstat/perfdata/monitor/PerfDataBufferImpl.java src/share/classes/sun/nio/ch/Net.java src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java src/share/classes/sun/nio/ch/SocketChannelImpl.java src/share/classes/sun/nio/cs/ext/ExtendedCharsets.java src/share/classes/sun/print/PSPrinterJob.java src/share/classes/sun/print/RasterPrinterJob.java src/share/classes/sun/rmi/server/Activation.java src/share/classes/sun/security/tools/jarsigner/Main.java src/share/classes/sun/security/tools/jarsigner/Resources.java src/share/classes/sun/security/tools/policytool/PolicyTool.java src/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java src/share/classes/sun/tools/jconsole/ProxyClient.java src/share/native/java/util/zip/ZipFile.c src/share/native/sun/security/jgss/wrapper/GSSLibStub.c src/share/native/sun/security/krb5/nativeccache.c src/solaris/classes/sun/print/CUPSPrinter.java src/solaris/classes/sun/print/IPPPrintService.java src/solaris/classes/sun/print/UnixPrintJob.java src/solaris/classes/sun/print/UnixPrintServiceLookup.java src/solaris/native/java/net/net_util_md.c src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c src/solaris/native/sun/nio/ch/Net.c src/windows/classes/sun/awt/windows/WPathGraphics.java src/windows/classes/sun/awt/windows/WToolkit.java src/windows/classes/sun/print/Win32PrintJob.java src/windows/native/java/net/DualStackPlainDatagramSocketImpl.c src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c src/windows/native/java/net/TwoStacksPlainSocketImpl.c src/windows/native/java/net/net_util_md.c src/windows/native/sun/nio/ch/Net.c test/java/lang/String/ToLowerCase.java test/java/lang/management/ThreadMXBean/SynchronizationStatistics.java
diffstat 4 files changed, 411 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Jul 21 23:07:40 2014 +0200
+++ b/.hgtags	Wed Jul 23 15:00:47 2014 -0400
@@ -146,6 +146,7 @@
 dda27c73d8db4a9c7a23872b6f0c5106edcb2021 jdk8-b22
 54202e0148ec7d4570cab5bc9b00d216a7677569 jdk8-b23
 34029a0c69bba882264a29fc822f8283fd15f104 jdk8-b24
+8da468cf037b6ea0eb2212dac7491efd59754d79 308-jdk8-sync
 ec17fbe5b8fbc52da070eec43b4711d9354b2ab8 jdk8-b25
 5aca406e87cb9144a9405be312dadd728a9c6fe2 jdk8-b26
 c68342532e2e7deb3a25fc04ed3e4c142278f747 jdk8-b27
--- a/src/share/classes/java/lang/reflect/Field.java	Mon Jul 21 23:07:40 2014 +0200
+++ b/src/share/classes/java/lang/reflect/Field.java	Wed Jul 23 15:00:47 2014 -0400
@@ -1163,5 +1163,5 @@
                                                        getDeclaringClass(),
                                                        getGenericType(),
                                                        TypeAnnotation.TypeAnnotationTarget.FIELD);
+    }
 }
-}
--- a/src/share/classes/java/lang/reflect/Modifier.java	Mon Jul 21 23:07:40 2014 +0200
+++ b/src/share/classes/java/lang/reflect/Modifier.java	Wed Jul 23 15:00:47 2014 -0400
@@ -357,7 +357,7 @@
     // that would allow the values to be treated as Java-level
     // constants since the values could be constant folded and updates
     // to the sets of modifiers missed. Thus, the fooModifiers()
-    // methods return an unchanging values for a given release, but a
+    // methods return an unchanging value for a given release, but a
     // value that can potentially change over time.
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/java/lang/UNIXProcess.java.bsd	Wed Jul 23 15:00:47 2014 -0400
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.security.AccessController;
+import static java.security.AccessController.doPrivileged;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * java.lang.Process subclass in the UNIX environment.
+ *
+ * @author Mario Wolczko and Ross Knippel.
+ * @author Konstantin Kladko (ported to Bsd)
+ * @author Martin Buchholz
+ */
+final class UNIXProcess extends Process {
+    private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
+        = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
+
+    private final int pid;
+    private int exitcode;
+    private boolean hasExited;
+
+    private /* final */ OutputStream stdin;
+    private /* final */ InputStream  stdout;
+    private /* final */ InputStream  stderr;
+
+    private static enum LaunchMechanism {
+        FORK(1),
+        POSIX_SPAWN(2);
+
+        private int value;
+        LaunchMechanism(int x) {value = x;}
+    };
+
+    /* On BSD, the default is to spawn */
+    private static final LaunchMechanism launchMechanism;
+    private static byte[] helperpath;
+
+    private static byte[] toCString(String s) {
+        if (s == null)
+            return null;
+        byte[] bytes = s.getBytes();
+        byte[] result = new byte[bytes.length + 1];
+        System.arraycopy(bytes, 0,
+                         result, 0,
+                         bytes.length);
+        result[result.length-1] = (byte)0;
+        return result;
+    }
+
+    static {
+        launchMechanism = AccessController.doPrivileged(
+                new PrivilegedAction<LaunchMechanism>()
+        {
+            public LaunchMechanism run() {
+                String javahome = System.getProperty("java.home");
+
+                helperpath = toCString(javahome + "/lib/jspawnhelper");
+                String s = System.getProperty(
+                    "jdk.lang.Process.launchMechanism", "posix_spawn");
+
+                try {
+                    return LaunchMechanism.valueOf(s.toUpperCase());
+                } catch (IllegalArgumentException e) {
+                    throw new Error(s + " is not a supported " +
+                        "process launch mechanism on this platform.");
+                }
+            }
+        });
+    }
+
+    /* this is for the reaping thread */
+    private native int waitForProcessExit(int pid);
+
+    /**
+     * Create a process. Depending on the mode flag, this is done by
+     * one of the following mechanisms.
+     * - fork(2) and exec(2)
+     * - posix_spawn(2)
+     *
+     * @param fds an array of three file descriptors.
+     *        Indexes 0, 1, and 2 correspond to standard input,
+     *        standard output and standard error, respectively.  On
+     *        input, a value of -1 means to create a pipe to connect
+     *        child and parent processes.  On output, a value which
+     *        is not -1 is the parent pipe fd corresponding to the
+     *        pipe which has been created.  An element of this array
+     *        is -1 on input if and only if it is <em>not</em> -1 on
+     *        output.
+     * @return the pid of the subprocess
+     */
+    private native int forkAndExec(int mode, byte[] helperpath,
+                                   byte[] prog,
+                                   byte[] argBlock, int argc,
+                                   byte[] envBlock, int envc,
+                                   byte[] dir,
+                                   int[] fds,
+                                   boolean redirectErrorStream)
+        throws IOException;
+
+    /**
+     * The thread factory used to create "process reaper" daemon threads.
+     */
+    private static class ProcessReaperThreadFactory implements ThreadFactory {
+        private final static ThreadGroup group = getRootThreadGroup();
+
+        private static ThreadGroup getRootThreadGroup() {
+            return doPrivileged(new PrivilegedAction<ThreadGroup> () {
+                public ThreadGroup run() {
+                    ThreadGroup root = Thread.currentThread().getThreadGroup();
+                    while (root.getParent() != null)
+                        root = root.getParent();
+                    return root;
+                }});
+        }
+
+        public Thread newThread(Runnable grimReaper) {
+            // Our thread stack requirement is quite modest.
+            Thread t = new Thread(group, grimReaper, "process reaper", 32768);
+            t.setDaemon(true);
+            // A small attempt (probably futile) to avoid priority inversion
+            t.setPriority(Thread.MAX_PRIORITY);
+            return t;
+        }
+    }
+
+    /**
+     * The thread pool of "process reaper" daemon threads.
+     */
+    private static final Executor processReaperExecutor =
+        doPrivileged(new PrivilegedAction<Executor>() {
+            public Executor run() {
+                return Executors.newCachedThreadPool
+                    (new ProcessReaperThreadFactory());
+            }});
+
+    UNIXProcess(final byte[] prog,
+                final byte[] argBlock, final int argc,
+                final byte[] envBlock, final int envc,
+                final byte[] dir,
+                final int[] fds,
+                final boolean redirectErrorStream)
+            throws IOException {
+
+        pid = forkAndExec(launchMechanism.value,
+                          helperpath,
+                          prog,
+                          argBlock, argc,
+                          envBlock, envc,
+                          dir,
+                          fds,
+                          redirectErrorStream);
+
+        try {
+            doPrivileged(new PrivilegedExceptionAction<Void>() {
+                public Void run() throws IOException {
+                    initStreams(fds);
+                    return null;
+                }});
+        } catch (PrivilegedActionException ex) {
+            throw (IOException) ex.getException();
+        }
+    }
+
+    static FileDescriptor newFileDescriptor(int fd) {
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        fdAccess.set(fileDescriptor, fd);
+        return fileDescriptor;
+    }
+
+    void initStreams(int[] fds) throws IOException {
+        stdin = (fds[0] == -1) ?
+            ProcessBuilder.NullOutputStream.INSTANCE :
+            new ProcessPipeOutputStream(fds[0]);
+
+        stdout = (fds[1] == -1) ?
+            ProcessBuilder.NullInputStream.INSTANCE :
+            new ProcessPipeInputStream(fds[1]);
+
+        stderr = (fds[2] == -1) ?
+            ProcessBuilder.NullInputStream.INSTANCE :
+            new ProcessPipeInputStream(fds[2]);
+
+        processReaperExecutor.execute(new Runnable() {
+            public void run() {
+                int exitcode = waitForProcessExit(pid);
+                UNIXProcess.this.processExited(exitcode);
+            }});
+    }
+
+    void processExited(int exitcode) {
+        synchronized (this) {
+            this.exitcode = exitcode;
+            hasExited = true;
+            notifyAll();
+        }
+
+        if (stdout instanceof ProcessPipeInputStream)
+            ((ProcessPipeInputStream) stdout).processExited();
+
+        if (stderr instanceof ProcessPipeInputStream)
+            ((ProcessPipeInputStream) stderr).processExited();
+
+        if (stdin instanceof ProcessPipeOutputStream)
+            ((ProcessPipeOutputStream) stdin).processExited();
+    }
+
+    public OutputStream getOutputStream() {
+        return stdin;
+    }
+
+    public InputStream getInputStream() {
+        return stdout;
+    }
+
+    public InputStream getErrorStream() {
+        return stderr;
+    }
+
+    public synchronized int waitFor() throws InterruptedException {
+        while (!hasExited) {
+            wait();
+        }
+        return exitcode;
+    }
+
+    @Override
+    public synchronized boolean waitFor(long timeout, TimeUnit unit)
+        throws InterruptedException
+    {
+        if (hasExited) return true;
+        if (timeout <= 0) return false;
+
+        long timeoutAsNanos = unit.toNanos(timeout);
+        long startTime = System.nanoTime();
+        long rem = timeoutAsNanos;
+
+        while (!hasExited && (rem > 0)) {
+            wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1));
+            rem = timeoutAsNanos - (System.nanoTime() - startTime);
+        }
+        return hasExited;
+    }
+
+    public synchronized int exitValue() {
+        if (!hasExited) {
+            throw new IllegalThreadStateException("process hasn't exited");
+        }
+        return exitcode;
+    }
+
+    private static native void destroyProcess(int pid, boolean force);
+    private void destroy(boolean force) {
+        // There is a risk that pid will be recycled, causing us to
+        // kill the wrong process!  So we only terminate processes
+        // that appear to still be running.  Even with this check,
+        // there is an unavoidable race condition here, but the window
+        // is very small, and OSes try hard to not recycle pids too
+        // soon, so this is quite safe.
+        synchronized (this) {
+            if (!hasExited)
+                destroyProcess(pid, force);
+        }
+        try { stdin.close();  } catch (IOException ignored) {}
+        try { stdout.close(); } catch (IOException ignored) {}
+        try { stderr.close(); } catch (IOException ignored) {}
+    }
+
+    public void destroy() {
+        destroy(false);
+    }
+
+    @Override
+    public Process destroyForcibly() {
+        destroy(true);
+        return this;
+    }
+
+    @Override
+    public synchronized boolean isAlive() {
+        return !hasExited;
+    }
+
+    private static native void init();
+
+    static {
+        init();
+    }
+
+    /**
+     * A buffered input stream for a subprocess pipe file descriptor
+     * that allows the underlying file descriptor to be reclaimed when
+     * the process exits, via the processExited hook.
+     *
+     * This is tricky because we do not want the user-level InputStream to be
+     * closed until the user invokes close(), and we need to continue to be
+     * able to read any buffered data lingering in the OS pipe buffer.
+     */
+    static class ProcessPipeInputStream extends BufferedInputStream {
+        private final Object closeLock = new Object();
+
+        ProcessPipeInputStream(int fd) {
+            super(new FileInputStream(newFileDescriptor(fd)));
+        }
+        private static byte[] drainInputStream(InputStream in)
+                throws IOException {
+            int n = 0;
+            int j;
+            byte[] a = null;
+            while ((j = in.available()) > 0) {
+                a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
+                n += in.read(a, n, j);
+            }
+            return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
+        }
+
+        /** Called by the process reaper thread when the process exits. */
+        synchronized void processExited() {
+            synchronized (closeLock) {
+                try {
+                    InputStream in = this.in;
+                    // this stream is closed if and only if: in == null
+                    if (in != null) {
+                        byte[] stragglers = drainInputStream(in);
+                        in.close();
+                        this.in = (stragglers == null) ?
+                            ProcessBuilder.NullInputStream.INSTANCE :
+                            new ByteArrayInputStream(stragglers);
+                    }
+                } catch (IOException ignored) {}
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+            // BufferedInputStream#close() is not synchronized unlike most other methods.
+            // Synchronizing helps avoid race with processExited().
+            synchronized (closeLock) {
+                super.close();
+            }
+        }
+    }
+
+    /**
+     * A buffered output stream for a subprocess pipe file descriptor
+     * that allows the underlying file descriptor to be reclaimed when
+     * the process exits, via the processExited hook.
+     */
+    static class ProcessPipeOutputStream extends BufferedOutputStream {
+        ProcessPipeOutputStream(int fd) {
+            super(new FileOutputStream(newFileDescriptor(fd)));
+        }
+
+        /** Called by the process reaper thread when the process exits. */
+        synchronized void processExited() {
+            OutputStream out = this.out;
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException ignored) {
+                    // We know of no reason to get an IOException, but if
+                    // we do, there's nothing else to do but carry on.
+                }
+                this.out = ProcessBuilder.NullOutputStream.INSTANCE;
+            }
+        }
+    }
+}