changeset 2635:99103b52aab0

. Make BSD change analagous to the Linux changes. . Add LIBDL as a variable (blank on BSD) and use it in a couple of places (it could certainly be used in more places to unify code further).
author Greg Lewis <glewis@eyesbeyond.com>
date Sat, 10 Jul 2010 21:27:19 -0700
parents 3f4f265e52c0
children 4ed60adf56d9
files make/common/Defs-bsd.gmk make/common/Defs-linux.gmk make/common/Defs-solaris.gmk make/sun/xawt/Makefile src/solaris/classes/java/lang/UNIXProcess.java.bsd src/solaris/classes/sun/tools/attach/BsdVirtualMachine.java
diffstat 6 files changed, 208 insertions(+), 142 deletions(-) [+]
line wrap: on
line diff
--- a/make/common/Defs-bsd.gmk	Sat Jul 10 12:46:13 2010 -0700
+++ b/make/common/Defs-bsd.gmk	Sat Jul 10 21:27:19 2010 -0700
@@ -336,7 +336,9 @@
 endif
 override LIBPOSIX4               =
 override LIBSOCKET               =
+override LIBNSL                  =
 override LIBTHREAD               =
+override LIBDL                   =
 override MOOT_PRIORITIES         = true
 override NO_INTERRUPTIBLE_IO     = true
 override OPENWIN_HOME            = $(X11_PATH)
--- a/make/common/Defs-linux.gmk	Sat Jul 10 12:46:13 2010 -0700
+++ b/make/common/Defs-linux.gmk	Sat Jul 10 21:27:19 2010 -0700
@@ -322,6 +322,7 @@
 override LIBSOCKET               =
 override LIBNSL                  =
 override LIBTHREAD               =
+override LIBDL                   = -ldl
 override MOOT_PRIORITIES         = true
 override NO_INTERRUPTIBLE_IO     = true
 override OPENWIN_HOME            = $(X11_PATH)
--- a/make/common/Defs-solaris.gmk	Sat Jul 10 12:46:13 2010 -0700
+++ b/make/common/Defs-solaris.gmk	Sat Jul 10 21:27:19 2010 -0700
@@ -648,6 +648,9 @@
 # Network Services library
 LIBNSL = -lnsl
 
+# Dynamic Loading library
+LIBDL = -ldl
+
 # GLOBAL_KPIC: If set means all libraries are PIC, position independent code
 #    EXCEPT for select compiles
 #    If a .o file is compiled non-PIC then it should be forced
--- a/make/sun/xawt/Makefile	Sat Jul 10 12:46:13 2010 -0700
+++ b/make/sun/xawt/Makefile	Sat Jul 10 21:27:19 2010 -0700
@@ -101,11 +101,6 @@
 vpath %.c   $(PLATFORM_SRC)/native/sun/java2d/opengl
 vpath %.c   $(PLATFORM_SRC)/native/sun/java2d/x11
 
-ifeq ($(PLATFORM), bsd)
-LIBDL=
-else
-LIBDL=-ldl
-endif
 OTHER_LDLIBS = $(LIBM) -lawt -lXext -lX11 -lXrender $(LIBDL) \
                    $(LDFLAGS_COMMON) $(AWT_RUNPATH) $(OTHER_LDFLAGS) -lXtst -lXi
 
--- a/src/solaris/classes/java/lang/UNIXProcess.java.bsd	Sat Jul 10 12:46:13 2010 -0700
+++ b/src/solaris/classes/java/lang/UNIXProcess.java.bsd	Sat Jul 10 21:27:19 2010 -0700
@@ -25,25 +25,42 @@
 
 package java.lang;
 
-import java.io.*;
+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.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 
-/* java.lang.Process subclass in the UNIX environment.
+/**
+ * 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 int pid;
+    private final int pid;
     private int exitcode;
     private boolean hasExited;
 
-    private OutputStream stdin_stream;
-    private InputStream  stdout_stream;
-    private InputStream  stderr_stream;
+    private /* final */ OutputStream stdin;
+    private /* final */ InputStream  stdout;
+    private /* final */ InputStream  stderr;
 
     /* this is for the reaping thread */
     private native int waitForProcessExit(int pid);
@@ -51,155 +68,136 @@
     /**
      * Create a process using fork(2) and exec(2).
      *
-     * @param std_fds array of 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.
+     * @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(byte[] prog,
                                    byte[] argBlock, int argc,
                                    byte[] envBlock, int envc,
                                    byte[] dir,
-                                   int[] std_fds,
+                                   int[] fds,
                                    boolean redirectErrorStream)
         throws IOException;
 
-    /* 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 */
+    /**
+     * The thread factory used to create "process reaper" daemon threads.
+     */
+    private static class ProcessReaperThreadFactory implements ThreadFactory {
+        private final static ThreadGroup group = getRootThreadGroup();
 
-    private static class Gate {
-
-        private boolean exited = false;
-        private IOException savedException;
-
-        synchronized void exit() { /* Opens the gate */
-           exited = true;
-           this.notify();
+        private static ThreadGroup getRootThreadGroup() {
+            return AccessController.doPrivileged
+            (new PrivilegedAction<ThreadGroup> () {
+            public ThreadGroup run() {
+                ThreadGroup root = Thread.currentThread().getThreadGroup();
+                while (root.getParent() != null)
+                    root = root.getParent();
+                return root;
+            }});
         }
 
-        synchronized void waitForExit() { /* wait until the gate is open */
-            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;
+        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
+        = 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[] std_fds,
+                final int[] fds,
                 final boolean redirectErrorStream)
-    throws IOException {
+            throws IOException {
 
-        final Gate gate = new 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).
-         */
+        pid = forkAndExec(prog,
+                          argBlock, argc,
+                          envBlock, envc,
+                          dir,
+                          fds,
+                          redirectErrorStream);
 
-        java.security.AccessController.doPrivileged(
-        new java.security.PrivilegedAction<Void>() {
-        public Void run() {
-            Thread t = new Thread("process reaper") {
-                    public void run() {
-                        try {
-                            pid = forkAndExec(prog,
-                                              argBlock, argc,
-                                              envBlock, envc,
-                                              dir,
-                                              std_fds,
-                                              redirectErrorStream);
-                        } catch (IOException e) {
-                            gate.setException(e); /*remember to rethrow later*/
-                            gate.exit();
-                            return;
-                        }
-                        java.security.AccessController.doPrivileged(
-                    new java.security.PrivilegedAction<Void>() {
-                    public Void run() {
-                        if (std_fds[0] == -1)
-                            stdin_stream = new ProcessBuilder.NullOutputStream();
-                        else {
-                            FileDescriptor stdin_fd = new FileDescriptor();
-                            fdAccess.set(stdin_fd, std_fds[0]);
-                            stdin_stream = new BufferedOutputStream(
-                                new FileOutputStream(stdin_fd));
-                        }
+        try {
+            AccessController.doPrivileged
+            (new PrivilegedExceptionAction<Void>() {
+                public Void run() throws IOException {
+                    initStreams(fds);
+                    return null;
+                }});
+        } catch (PrivilegedActionException ex) {
+            throw (IOException) ex.getException();
+        }
+    }
 
-                        if (std_fds[1] == -1)
-                            stdout_stream = new ProcessBuilder.NullInputStream();
-                        else {
-                            FileDescriptor stdout_fd = new FileDescriptor();
-                            fdAccess.set(stdout_fd, std_fds[1]);
-                            stdout_stream = new BufferedInputStream(
-                                new FileInputStream(stdout_fd));
-                        }
+    static FileDescriptor newFileDescriptor(int fd) {
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        fdAccess.set(fileDescriptor, fd);
+        return fileDescriptor;
+    }
 
-                        if (std_fds[2] == -1)
-                            stderr_stream = new ProcessBuilder.NullInputStream();
-                        else {
-                            FileDescriptor stderr_fd = new FileDescriptor();
-                            fdAccess.set(stderr_fd, std_fds[2]);
-                            stderr_stream = new FileInputStream(stderr_fd);
-                        }
+    void initStreams(int[] fds) throws IOException {
+        stdin = (fds[0] == -1) ?
+            ProcessBuilder.NullOutputStream.INSTANCE :
+            new ProcessPipeOutputStream(fds[0]);
 
-                        return null; }});
-                        gate.exit(); /* exit from constructor */
-                        int res = waitForProcessExit(pid);
-                        synchronized (UNIXProcess.this) {
-                            hasExited = true;
-                            exitcode = res;
-                            UNIXProcess.this.notifyAll();
-                        }
-                    }
-                };
-                t.setDaemon(true);
-                t.start();
-                return null; }});
-        gate.waitForExit();
-        IOException e = gate.getException();
-        if (e != null)
-            throw new IOException(e.toString());
+        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);
+            }});
+    }
+
+    synchronized void processExited(int exitcode) {
+        if (stdout instanceof ProcessPipeInputStream)
+            ((ProcessPipeInputStream) stdout).processExited();
+
+        if (stderr instanceof ProcessPipeInputStream)
+            ((ProcessPipeInputStream) stderr).processExited();
+
+        if (stdin instanceof ProcessPipeOutputStream)
+            ((ProcessPipeOutputStream) stdin).processExited();
+
+        this.exitcode = exitcode;
+        hasExited = true;
+        notifyAll();
     }
 
     public OutputStream getOutputStream() {
-        return stdin_stream;
+        return stdin;
     }
 
     public InputStream getInputStream() {
-        return stdout_stream;
+        return stdout;
     }
 
     public InputStream getErrorStream() {
-        return stderr_stream;
+        return stderr;
     }
 
     public synchronized int waitFor() throws InterruptedException {
@@ -228,13 +226,9 @@
             if (!hasExited)
                 destroyProcess(pid);
         }
-        try {
-            stdin_stream.close();
-            stdout_stream.close();
-            stderr_stream.close();
-        } catch (IOException e) {
-            // ignore
-        }
+        try { stdin.close();  } catch (IOException ignored) {}
+        try { stdout.close(); } catch (IOException ignored) {}
+        try { stderr.close(); } catch (IOException ignored) {}
     }
 
     /* This routine initializes JNI field offsets for the class */
@@ -243,4 +237,77 @@
     static {
         initIDs();
     }
+
+    /**
+     * 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 {
+        ProcessPipeInputStream(int fd) {
+            super(new FileInputStream(newFileDescriptor(fd)));
+        }
+
+        private static byte[] drainInputStream(InputStream in)
+                throws IOException {
+            if (in == null) return null;
+            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() {
+            // Most BufferedInputStream methods are synchronized, but close()
+            // is not, and so we have to handle concurrent racing close().
+            try {
+                InputStream in = this.in;
+                if (in != null) {
+                    byte[] stragglers = drainInputStream(in);
+                    in.close();
+                    this.in = (stragglers == null) ?
+                        ProcessBuilder.NullInputStream.INSTANCE :
+                        new ByteArrayInputStream(stragglers);
+                    if (buf == null) // asynchronous close()?
+                        this.in = null;
+                }
+            } catch (IOException ignored) {
+                // probably an asynchronous 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;
+            }
+        }
+    }
 }
--- a/src/solaris/classes/sun/tools/attach/BsdVirtualMachine.java	Sat Jul 10 12:46:13 2010 -0700
+++ b/src/solaris/classes/sun/tools/attach/BsdVirtualMachine.java	Sat Jul 10 21:27:19 2010 -0700
@@ -37,6 +37,8 @@
  * Bsd implementation of HotSpotVirtualMachine
  */
 public class BsdVirtualMachine extends HotSpotVirtualMachine {
+    // temp directory for socket file
+    private static final String tmpdir = System.getProperty("java.io.tmpdir");
 
     // The patch to the socket file created by the target VM
     String path;
@@ -62,7 +64,7 @@
         // Then we attempt to find the socket file again.
         path = findSocketFile(pid);
         if (path == null) {
-            File f = new File("/tmp/.attach_pid" + pid);
+            File f = new File(tmpdir, ".attach_pid" + pid);
 	    createAttachFile(f.getPath());
             try {
                 sendQuitTo(pid);
@@ -242,17 +244,13 @@
 
     // Return the socket file for the given process.
     // Checks working directory of process for .java_pid<pid>. If not
-    // found it looks in /tmp.
+    // found it looks in temp directory.
     private String findSocketFile(int pid) {
         // First check for a .java_pid<pid> file in the working directory
         // of the target process
         String fn = ".java_pid" + pid;
-        String path = "/tmp/" + fn;
-        File f = new File(path);
-        if (!f.exists()) {
-            return null;            // not found
-        }
-        return path;
+        File f = new File(tmpdir, path);
+	return f.exists() ? f.getPath() : null;
     }
 
     /*