Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6209585

(process) java.lang.UNIXProcess should use thread pooling

    Details

    • Type: Enhancement
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 5.0
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:

      Description

      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

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              gmanwanisunw Girish Manwani (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Imported:
                Indexed: