alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (UNIXProcess.java.solaris)

This example Java source code file (UNIXProcess.java.solaris) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bufferedinputstream, deferredcloseinputstream, filedescriptor, inputstream, interruptedexception, ioexception, launchmechanism, object, outputstream, override, process, security, string, threading, threads, unixprocess, void

The UNIXProcess.java.solaris Java example source code

/*
 * 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.*;
import java.util.concurrent.TimeUnit;
import java.security.AccessController;
import java.security.PrivilegedAction;

/* java.lang.Process subclass in the UNIX environment.
 *
 * @author Mario Wolczko and Ross Knippel.
 */

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 OutputStream stdin_stream;
    private InputStream stdout_stream;
    private DeferredCloseInputStream stdout_inner_stream;
    private InputStream stderr_stream;

    private static enum LaunchMechanism {
        FORK(1),
        POSIX_SPAWN(2);

        private int value;
        LaunchMechanism(int x) {value = x;}
    };

    /* On Solaris, 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");
                String osArch = System.getProperty("os.arch");
                if (osArch.equals("x86")) {
                    osArch = "i386";
                } else if (osArch.equals("x86_64")) {
                    osArch = "amd64";
                }

                helperpath = toCString(javahome + "/lib/" + osArch + "/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 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 -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[] std_fds,
                                   boolean redirectErrorStream)
        throws IOException;

    UNIXProcess(final byte[] prog,
                final byte[] argBlock, int argc,
                final byte[] envBlock, int envc,
                final byte[] dir,
                final int[] std_fds,
                final boolean redirectErrorStream)
    throws IOException {
        pid = forkAndExec(launchMechanism.value,
                          helperpath,
                          prog,
                          argBlock, argc,
                          envBlock, envc,
                          dir,
                          std_fds,
                          redirectErrorStream);

        java.security.AccessController.doPrivileged(
        new java.security.PrivilegedAction<Void>() { public Void run() {
            if (std_fds[0] == -1)
                stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
            else {
                FileDescriptor stdin_fd = new FileDescriptor();
                fdAccess.set(stdin_fd, std_fds[0]);
                stdin_stream = new BufferedOutputStream(
                    new FileOutputStream(stdin_fd));
            }

            if (std_fds[1] == -1)
                stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
            else {
                FileDescriptor stdout_fd = new FileDescriptor();
                fdAccess.set(stdout_fd, std_fds[1]);
                stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
                stdout_stream = new BufferedInputStream(stdout_inner_stream);
            }

            if (std_fds[2] == -1)
                stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
            else {
                FileDescriptor stderr_fd = new FileDescriptor();
                fdAccess.set(stderr_fd, std_fds[2]);
                stderr_stream = new DeferredCloseInputStream(stderr_fd);
            }

            return null; }});

        /*
         * 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).
         */

        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() { public Void run() {
                Thread t = new Thread("process reaper") {
                    public void run() {
                        int res = waitForProcessExit(pid);
                        synchronized (UNIXProcess.this) {
                            hasExited = true;
                            exitcode = res;
                            UNIXProcess.this.notifyAll();
                        }
                    }
                };
                t.setDaemon(true);
                t.start();
                return null; }});
    }

    public OutputStream getOutputStream() {
        return stdin_stream;
    }

    public InputStream getInputStream() {
        return stdout_stream;
    }

    public InputStream getErrorStream() {
        return stderr_stream;
    }

    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 synchronized 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.
        if (!hasExited)
            destroyProcess(pid, force);
        try {
            stdin_stream.close();
            if (stdout_inner_stream != null)
                stdout_inner_stream.closeDeferred(stdout_stream);
            if (stderr_stream instanceof DeferredCloseInputStream)
                ((DeferredCloseInputStream) stderr_stream)
                    .closeDeferred(stderr_stream);
        } catch (IOException e) {
            // ignore
        }
    }

    public void destroy() {
        destroy(false);
    }

    @Override
    public Process destroyForcibly() {
        destroy(true);
        return this;
    }

    @Override
    public synchronized boolean isAlive() {
        return !hasExited;
    }

    // A FileInputStream that supports the deferment of the actual close
    // operation until the last pending I/O operation on the stream has
    // finished.  This is required on Solaris because we must close the stdin
    // and stdout streams in the destroy method in order to reclaim the
    // underlying file descriptors.  Doing so, however, causes any thread
    // currently blocked in a read on one of those streams to receive an
    // IOException("Bad file number"), which is incompatible with historical
    // behavior.  By deferring the close we allow any pending reads to see -1
    // (EOF) as they did before.
    //
    private static class DeferredCloseInputStream
        extends FileInputStream
    {

        private DeferredCloseInputStream(FileDescriptor fd) {
            super(fd);
        }

        private Object lock = new Object();     // For the following fields
        private boolean closePending = false;
        private int useCount = 0;
        private InputStream streamToClose;

        private void raise() {
            synchronized (lock) {
                useCount++;
            }
        }

        private void lower() throws IOException {
            synchronized (lock) {
                useCount--;
                if (useCount == 0 && closePending) {
                    streamToClose.close();
                }
            }
        }

        // stc is the actual stream to be closed; it might be this object, or
        // it might be an upstream object for which this object is downstream.
        //
        private void closeDeferred(InputStream stc) throws IOException {
            synchronized (lock) {
                if (useCount == 0) {
                    stc.close();
                } else {
                    closePending = true;
                    streamToClose = stc;
                }
            }
        }

        public void close() throws IOException {
            synchronized (lock) {
                useCount = 0;
                closePending = false;
            }
            super.close();
        }

        public int read() throws IOException {
            raise();
            try {
                return super.read();
            } finally {
                lower();
            }
        }

        public int read(byte[] b) throws IOException {
            raise();
            try {
                return super.read(b);
            } finally {
                lower();
            }
        }

        public int read(byte[] b, int off, int len) throws IOException {
            raise();
            try {
                return super.read(b, off, len);
            } finally {
                lower();
            }
        }

        public long skip(long n) throws IOException {
            raise();
            try {
                return super.skip(n);
            } finally {
                lower();
            }
        }

        public int available() throws IOException {
            raise();
            try {
                return super.available();
            } finally {
                lower();
            }
        }

    }

    private static native void init();

    static {
        init();
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java UNIXProcess.java.solaris source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.