This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Patch: Win32 Process Implementation


Hi,

    Here is a simple implementation of java.lang.Process for Win32.
It works for simple test cases and well enough to produce
symbolic information in stacktraces via addr2line and c++filt, the
need for which in fact prompted this effort in the first place!

It uses anonymous pipes to redirect the standard streams of the
spawned child process.

A few things to be noted:

1. The current Win32Process.java says that we must remember to make
   handles non-inheritable when we implement this. AFAICT, Win32
   handles by default are non-inheritable unless explicitly made
   so (which we are not doing), so this does not apply.

2. ReadFile( ) on Win32 gives an ERROR_BROKEN_PIPE when the parent
   process is reading from the child's output/error streams and the
   child exits, unlike read( ) on Unix which simply indicates an
   EOF. This causes an IOException in GCJ, which does not happen BTW,
   in Sun's JDK. I would suggest that we modify natFileDescriptorWin32.cc
   to return -1 (EOF) in read( ) when it encounters an ERROR_BROKEN_PIPE
   on a ReadFile( ), to avoid this error.

3. As noted in the JavaDoc for Process in Sun's JDK as well, this
   does not work well for non-console Win32 or DOS/Win16 applications.

Finally, Tom I hope this patch's format is acceptable to you.
Since I do not use CVS, the "Index: " lines have been generated by
the same script that produces the diffs and there aren't the "diff"
and "RCS" lines after the "Index: " lines that are generated by CVS.
However, I think that should not have a negative impact.

Ranjit.

Index: ChangeLog
from  Ranjit Mathew  <rmathew@hotmail.com>

	* java/lang/Win32Process.java (destroy): Declare as native.
	(hasExited): New native method.
	(exitValue): Define.
	(getErrorStream): Likewise.
	(getInputStream): Likewise.
	(getOutputStream): Likewise.
	(waitFor): Declare as native.
	(startProcess): New native method.
	(cleanup): Likewise.
	(ConcreteProcess): Define.
	(outputStream, inputStream, errorStream): New members.
	(procHandle, exitCode): Likewise.

	* java/lang/natWin32Process.cc
	(java::lang::ConcreteProcess::cleanup): Define.
	(java::lang::ConcreteProcess::destroy): Likewise.
	(java::lang::ConcreteProcess::hasExited): Likewise.
	(java::lang::ConcreteProcess::waitFor): Likewise.
	(new_string): Likewise.
	(java::lang::ConcreteProcess::startProcess): Likewise.

Index: java/lang/Win32Process.java
===================================================================
--- java/lang/Win32Process.java	2003-02-04 06:19:36.000000000 +0530
+++ java/lang/Win32Process.java	2003-02-06 23:14:38.000000000 +0530
@@ -1,5 +1,5 @@
 // Win32Process.java - Subclass of Process for Win32 systems.

-/* Copyright (C) 2002  Free Software Foundation
+/* Copyright (C) 2002, 2003  Free Software Foundation

    This file is part of libgcj.
@@ -23,42 +23,43 @@
 // This is entirely internal to our implementation.

-// NOTE: when this is implemented, we'll need to add
-// HANDLE_FLAG_INHERIT in FileDescriptor and other places, to make
-// sure that file descriptors aren't inherited by the child process.
-// See _Jv_platform_close_on_exec.
-
 // 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
 {
-  public void destroy ()
-  {
-    throw new Error("not implemented");
-  }
-
+  public native void destroy ();
+
+  public native boolean hasExited ();
+
   public int exitValue ()
   {
-    throw new Error("not implemented");
+    if (! hasExited ())
+      throw new IllegalThreadStateException ("Process has not exited");
+
+    return exitCode;
   }

   public InputStream getErrorStream ()
   {
-    throw new Error("not implemented");
+    return errorStream;
   }

   public InputStream getInputStream ()
   {
-    throw new Error("not implemented");
+    return inputStream;
   }

   public OutputStream getOutputStream ()
   {
-    throw new Error("not implemented");
+    return outputStream;
   }

-  public int waitFor () throws InterruptedException
-  {
-    throw new Error("not implemented");
-  }
+  public native int waitFor () throws InterruptedException;
+
+  public native void startProcess (String[] progarray,
+                                   String[] envp,
+                                   File dir)
+    throws IOException;
+
+  public native void cleanup ();

   public ConcreteProcess (String[] progarray,
@@ -67,6 +68,17 @@
     throws IOException
   {
-    throw new IOException("not implemented");
+    startProcess (progarray, envp, dir);
   }

+  // The standard streams (stdin, stdout and stderr, respectively)
+  // of the child as seen by the parent process.
+  private OutputStream outputStream;
+  private InputStream inputStream;
+  private InputStream errorStream;
+
+  // Handle to the child process - cast to HANDLE before use.
+  private int procHandle;
+
+  // Exit code of the child if it has exited.
+  private int exitCode;
 }
Index: java/lang/natWin32Process.cc
===================================================================
--- java/lang/natWin32Process.cc	2003-02-04 06:28:55.000000000 +0530
+++ java/lang/natWin32Process.cc	2003-02-07 01:28:23.000000000 +0530
@@ -0,0 +1,294 @@
+// natWin32Process.cc - Native side of Win32 process code.
+
+/* Copyright (C) 2003  Free Software Foundation
+
+   This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
+details.  */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// Conflicts with the definition in "java/lang/reflect/Modifier.h"
+#undef STRICT
+
+#include <gcj/cni.h>
+#include <jvm.h>
+
+#include <java/lang/ConcreteProcess.h>
+#include <java/lang/IllegalThreadStateException.h>
+#include <java/lang/InterruptedException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/Thread.h>
+#include <java/io/File.h>
+#include <java/io/FileDescriptor.h>
+#include <java/io/FileInputStream.h>
+#include <java/io/FileOutputStream.h>
+#include <java/io/IOException.h>
+#include <java/lang/OutOfMemoryError.h>
+
+void
+java::lang::ConcreteProcess::cleanup (void)
+{
+  if (inputStream != NULL)
+    {
+      inputStream->close ();
+      inputStream = NULL;
+    }
+
+  if (outputStream != NULL)
+    {
+      outputStream->close ();
+      outputStream = NULL;
+    }
+
+  if (errorStream != NULL)
+    {
+      errorStream->close ();
+      errorStream = NULL;
+    }
+}
+
+void
+java::lang::ConcreteProcess::destroy (void)
+{
+  if (! hasExited ())
+    {
+      // Kill it forcibly and assign an (arbitrary) exit code of 0.
+      TerminateProcess ((HANDLE) procHandle, 0);
+      exitCode = 0;
+
+      cleanup ();
+    }
+}
+
+jboolean
+java::lang::ConcreteProcess::hasExited (void)
+{
+  DWORD exitStatus;
+
+  if (GetExitCodeProcess ((HANDLE) procHandle, &exitStatus) != 0)
+    {
+      // NOTE: STILL_ACTIVE is defined as "259" by Win32 - if the
+      // child actually exits with this return code, we have a
+      // problem here. See MSDN documentation on GetExitCodeProcess( ).
+
+      if (exitStatus == STILL_ACTIVE)
+        return false;
+      else
+        {
+          cleanup ();
+          exitCode = exitStatus;
+          return true;
+        }
+    }
+  else
+    return true;
+}
+
+jint
+java::lang::ConcreteProcess::waitFor (void)
+{
+  if (! hasExited ())
+    {
+      DWORD exitStatus = 0UL;
+
+      // FIXME: The wait should be interruptible.
+      WaitForSingleObject ((HANDLE) procHandle, INFINITE);
+
+      GetExitCodeProcess ((HANDLE) procHandle, &exitStatus);
+      exitCode = exitStatus;
+
+      cleanup ();
+    }
+
+  return exitCode;
+}
+
+static char *
+new_string (jstring string)
+{
+  jsize s = _Jv_GetStringUTFLength (string);
+  char *buf = (char *) _Jv_Malloc (s + 1);
+  _Jv_GetStringUTFRegion (string, 0, s, buf);
+  buf[s] = '\0';
+  return buf;
+}
+
+void
+java::lang::ConcreteProcess::startProcess (jstringArray progarray,
+                                           jstringArray envp,
+                                           java::io::File *dir)
+{
+  using namespace java::io;
+
+  procHandle = (jint) INVALID_HANDLE_VALUE;
+
+  // Reconstruct the command line.
+  jstring *elts = elements (progarray);
+
+  int cmdLineLen = 0;
+
+  for (int i = 0; i < progarray->length; ++i)
+    cmdLineLen += (_Jv_GetStringUTFLength (elts[i]) + 1);
+
+  char *cmdLine = (char *) _Jv_Malloc (cmdLineLen + 1);
+
+  int j = 0;
+  for (int i = 0; i < progarray->length; ++i)
+    {
+      jsize s = _Jv_GetStringUTFLength (elts[i]);
+      _Jv_GetStringUTFRegion (elts[i], 0, s, (cmdLine + j));
+
+      j += s;
+      *(cmdLine + j) = ' ';
+      j++;
+    }
+  *(cmdLine + j) = '\0';
+
+  // Get the environment, if any.
+  char *env = NULL;
+  if (envp)
+    {
+      elts = elements (envp);
+
+      int envLen = 0;
+      for (int i = 0; i < envp->length; ++i)
+        envLen += (_Jv_GetStringUTFLength (elts[i]) + 1);
+
+      env = (char *) _Jv_Malloc (envLen + 1);
+
+      int j = 0;
+      for (int i = 0; i < envp->length; ++i)
+        {
+          jsize s = _Jv_GetStringUTFLength (elts[i]);
+          _Jv_GetStringUTFRegion (elts[i], 0, s, (env + j));
+
+          j += s;
+          *(env + j) = '\0';
+          j++;
+        }
+      *(env + j) = '\0';
+    }
+
+  // Get the working directory path, if specified.
+  char *wdir = NULL;
+  if (dir != NULL)
+    wdir = new_string (dir->getPath ());
+
+  errorStream = NULL;
+  inputStream = NULL;
+  outputStream = NULL;
+
+  java::lang::Throwable *exc = NULL;
+
+  try
+    {
+      // We create anonymous pipes to communicate with the child
+      // on each of standard streams.
+
+      HANDLE cldStdInRd, cldStdInWr;
+      HANDLE cldStdOutRd, cldStdOutWr;
+      HANDLE cldStdErrRd, cldStdErrWr;
+
+      SECURITY_ATTRIBUTES sAttrs;
+
+      // Explicitly allow the handles to the pipes to be inherited.
+      sAttrs.nLength = sizeof (SECURITY_ATTRIBUTES);
+      sAttrs.bInheritHandle = 1;
+      sAttrs.lpSecurityDescriptor = NULL;
+
+
+      char tmpBuff[64];
+      if (CreatePipe (&cldStdInRd, &cldStdInWr, &sAttrs, 0) == 0)
+        {
+          sprintf (tmpBuff,
+                   "Error creating stdin pipe (Win32 Error Code: %lu)",
+                   GetLastError ());
+          throw new IOException (JvNewStringLatin1 (tmpBuff));
+        }
+
+      if (CreatePipe (&cldStdOutRd, &cldStdOutWr, &sAttrs, 0) == 0)
+        {
+          sprintf (tmpBuff,
+                   "Error creating stdout pipe (Win32 Error Code: %lu)",
+                   GetLastError ());
+          throw new IOException (JvNewStringLatin1 (tmpBuff));
+        }
+
+      if (CreatePipe (&cldStdErrRd, &cldStdErrWr, &sAttrs, 0) == 0)
+        {
+          sprintf (tmpBuff,
+                   "Error creating stderr pipe (Win32 Error Code: %lu)",
+                   GetLastError ());
+          throw new IOException (JvNewStringLatin1 (tmpBuff));
+        }
+
+      outputStream = new FileOutputStream
+                         (new FileDescriptor ((jint) cldStdInWr));
+      inputStream = new FileInputStream
+                        (new FileDescriptor ((jint) cldStdOutRd));
+      errorStream = new FileInputStream
+                        (new FileDescriptor ((jint) cldStdErrRd));
+
+      // Now create the child process.
+      PROCESS_INFORMATION pi;
+      STARTUPINFO si;
+
+      ZeroMemory (&pi, sizeof (PROCESS_INFORMATION));
+
+      ZeroMemory (&si, sizeof (STARTUPINFO));
+      si.cb = sizeof (STARTUPINFO);
+
+      // Explicitly specify the handles to the standard streams.
+      si.dwFlags |= STARTF_USESTDHANDLES;
+
+      si.hStdInput = cldStdInRd;
+      si.hStdOutput = cldStdOutWr;
+      si.hStdError = cldStdErrWr;
+
+      if (CreateProcess (NULL,
+                         cmdLine,
+                         NULL,
+                         NULL,
+                         1,
+                         0,
+                         env,
+                         wdir,
+                         &si,
+                         &pi) == 0)
+        {
+          sprintf (tmpBuff,
+                   "Error creating child process (Win32 Error Code: %lu)",
+                   GetLastError ());
+          throw new IOException (JvNewStringLatin1 (tmpBuff));
+        }
+
+      procHandle = (jint ) pi.hProcess;
+
+      // Close the wrong ends (for the parent) of the pipes.
+      CloseHandle (cldStdInRd);
+      CloseHandle (cldStdOutWr);
+      CloseHandle (cldStdErrWr);
+
+      _Jv_Free (cmdLine);
+      if (env != NULL)
+        _Jv_Free (env);
+      if (wdir != NULL)
+        _Jv_Free (wdir);
+    }
+  catch (java::lang::Throwable *thrown)
+    {
+      cleanup ();
+      exc = thrown;
+    }
+
+  if (exc != NULL)
+    throw exc;
+}



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]