< prev index next >

src/java.base/unix/classes/java/lang/ProcessImpl.java

Print this page
rev 59105 : imported patch corelibs


  60  * @author Martin Buchholz
  61  * @author Volker Simonis (ported to AIX)
  62  * @since   1.5
  63  */
  64 final class ProcessImpl extends Process {
  65     private static final JavaIOFileDescriptorAccess fdAccess
  66         = SharedSecrets.getJavaIOFileDescriptorAccess();
  67 
  68     // Linux platforms support a normal (non-forcible) kill signal.
  69     static final boolean SUPPORTS_NORMAL_TERMINATION = true;
  70 
  71     private final int pid;
  72     private final ProcessHandleImpl processHandle;
  73     private int exitcode;
  74     private boolean hasExited;
  75 
  76     private /* final */ OutputStream stdin;
  77     private /* final */ InputStream  stdout;
  78     private /* final */ InputStream  stderr;
  79 
  80     // only used on Solaris
  81     private /* final */ DeferredCloseInputStream stdout_inner_stream;
  82 
  83     private static enum LaunchMechanism {
  84         // order IS important!
  85         FORK,
  86         POSIX_SPAWN,
  87         VFORK
  88     }
  89 
  90     private static enum Platform {
  91 
  92         LINUX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.VFORK, LaunchMechanism.FORK),
  93 
  94         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  95 
  96         SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  97 
  98         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  99 
 100         final LaunchMechanism defaultLaunchMechanism;
 101         final Set<LaunchMechanism> validLaunchMechanisms;
 102 
 103         Platform(LaunchMechanism ... launchMechanisms) {
 104             this.defaultLaunchMechanism = launchMechanisms[0];
 105             this.validLaunchMechanisms =
 106                 EnumSet.copyOf(Arrays.asList(launchMechanisms));
 107         }
 108 
 109         LaunchMechanism launchMechanism() {
 110             return AccessController.doPrivileged(
 111                 (PrivilegedAction<LaunchMechanism>) () -> {
 112                     String s = System.getProperty(
 113                         "jdk.lang.Process.launchMechanism");
 114                     LaunchMechanism lm;
 115                     if (s == null) {
 116                         lm = defaultLaunchMechanism;
 117                         s = lm.name().toLowerCase(Locale.ENGLISH);


 122                         } catch (IllegalArgumentException e) {
 123                             lm = null;
 124                         }
 125                     }
 126                     if (lm == null || !validLaunchMechanisms.contains(lm)) {
 127                         throw new Error(
 128                             s + " is not a supported " +
 129                             "process launch mechanism on this platform."
 130                         );
 131                     }
 132                     return lm;
 133                 }
 134             );
 135         }
 136 
 137         static Platform get() {
 138             String osName = GetPropertyAction.privilegedGetProperty("os.name");
 139 
 140             if (osName.equals("Linux")) { return LINUX; }
 141             if (osName.contains("OS X")) { return BSD; }
 142             if (osName.equals("SunOS")) { return SOLARIS; }
 143             if (osName.equals("AIX")) { return AIX; }
 144 
 145             throw new Error(osName + " is not a supported OS platform.");
 146         }
 147     }
 148 
 149     private static final Platform platform = Platform.get();
 150     private static final LaunchMechanism launchMechanism = platform.launchMechanism();
 151     private static final byte[] helperpath = toCString(StaticProperty.javaHome() + "/lib/jspawnhelper");
 152 
 153     private static byte[] toCString(String s) {
 154         if (s == null)
 155             return null;
 156         byte[] bytes = s.getBytes();
 157         byte[] result = new byte[bytes.length + 1];
 158         System.arraycopy(bytes, 0,
 159                          result, 0,
 160                          bytes.length);
 161         result[result.length-1] = (byte)0;
 162         return result;


 368                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 369                     synchronized (this) {
 370                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 371                         this.hasExited = true;
 372                         this.notifyAll();
 373                     }
 374 
 375                     if (stdout instanceof ProcessPipeInputStream)
 376                         ((ProcessPipeInputStream) stdout).processExited();
 377 
 378                     if (stderr instanceof ProcessPipeInputStream)
 379                         ((ProcessPipeInputStream) stderr).processExited();
 380 
 381                     if (stdin instanceof ProcessPipeOutputStream)
 382                         ((ProcessPipeOutputStream) stdin).processExited();
 383 
 384                     return null;
 385                 });
 386                 break;
 387 
 388             case SOLARIS:
 389                 stdin = (fds[0] == -1) ?
 390                         ProcessBuilder.NullOutputStream.INSTANCE :
 391                         new BufferedOutputStream(
 392                             new FileOutputStream(newFileDescriptor(fds[0])));
 393 
 394                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 395                          ProcessBuilder.NullInputStream.INSTANCE :
 396                          new BufferedInputStream(
 397                              stdout_inner_stream =
 398                                  new DeferredCloseInputStream(
 399                                      newFileDescriptor(fds[1])));
 400 
 401                 stderr = (fds[2] == -1) ?
 402                          ProcessBuilder.NullInputStream.INSTANCE :
 403                          new DeferredCloseInputStream(newFileDescriptor(fds[2]));
 404 
 405                 /*
 406                  * For each subprocess forked a corresponding reaper task
 407                  * is submitted.  That task is the only thread which waits
 408                  * for the subprocess to terminate and it doesn't hold any
 409                  * locks while doing so.  This design allows waitFor() and
 410                  * exitStatus() to be safely executed in parallel (and they
 411                  * need no native code).
 412                  */
 413                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 414                     synchronized (this) {
 415                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 416                         this.hasExited = true;
 417                         this.notifyAll();
 418                     }
 419                     return null;
 420                 });
 421                 break;
 422 
 423             case AIX:
 424                 stdin = (fds[0] == -1) ?
 425                         ProcessBuilder.NullOutputStream.INSTANCE :
 426                         new ProcessPipeOutputStream(fds[0]);
 427 
 428                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 429                          ProcessBuilder.NullInputStream.INSTANCE :
 430                          new DeferredCloseProcessPipeInputStream(fds[1]);
 431 
 432                 stderr = (fds[2] == -1) ?
 433                          ProcessBuilder.NullInputStream.INSTANCE :
 434                          new DeferredCloseProcessPipeInputStream(fds[2]);
 435 
 436                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 437                     synchronized (this) {
 438                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 439                         this.hasExited = true;
 440                         this.notifyAll();
 441                     }
 442 


 503     }
 504 
 505     private void destroy(boolean force) {
 506         switch (platform) {
 507             case LINUX:
 508             case BSD:
 509             case AIX:
 510                 // There is a risk that pid will be recycled, causing us to
 511                 // kill the wrong process!  So we only terminate processes
 512                 // that appear to still be running.  Even with this check,
 513                 // there is an unavoidable race condition here, but the window
 514                 // is very small, and OSes try hard to not recycle pids too
 515                 // soon, so this is quite safe.
 516                 synchronized (this) {
 517                     if (!hasExited)
 518                         processHandle.destroyProcess(force);
 519                 }
 520                 try { stdin.close();  } catch (IOException ignored) {}
 521                 try { stdout.close(); } catch (IOException ignored) {}
 522                 try { stderr.close(); } catch (IOException ignored) {}
 523                 break;
 524 
 525             case SOLARIS:
 526                 // There is a risk that pid will be recycled, causing us to
 527                 // kill the wrong process!  So we only terminate processes
 528                 // that appear to still be running.  Even with this check,
 529                 // there is an unavoidable race condition here, but the window
 530                 // is very small, and OSes try hard to not recycle pids too
 531                 // soon, so this is quite safe.
 532                 synchronized (this) {
 533                     if (!hasExited)
 534                         processHandle.destroyProcess(force);
 535                     try {
 536                         stdin.close();
 537                         if (stdout_inner_stream != null)
 538                             stdout_inner_stream.closeDeferred(stdout);
 539                         if (stderr instanceof DeferredCloseInputStream)
 540                             ((DeferredCloseInputStream) stderr)
 541                                 .closeDeferred(stderr);
 542                     } catch (IOException e) {
 543                         // ignore
 544                     }
 545                 }
 546                 break;
 547 
 548             default: throw new AssertionError("Unsupported platform: " + platform);
 549         }
 550     }
 551 
 552     @Override
 553     public CompletableFuture<Process> onExit() {
 554         return ProcessHandleImpl.completion(pid, false)
 555                 .handleAsync((unusedExitStatus, unusedThrowable) -> {
 556                     boolean interrupted = false;
 557                     while (true) {
 558                         // Ensure that the concurrent task setting the exit status has completed
 559                         try {
 560                             waitFor();
 561                             break;
 562                         } catch (InterruptedException ie) {
 563                             interrupted = true;
 564                         }
 565                     }




  60  * @author Martin Buchholz
  61  * @author Volker Simonis (ported to AIX)
  62  * @since   1.5
  63  */
  64 final class ProcessImpl extends Process {
  65     private static final JavaIOFileDescriptorAccess fdAccess
  66         = SharedSecrets.getJavaIOFileDescriptorAccess();
  67 
  68     // Linux platforms support a normal (non-forcible) kill signal.
  69     static final boolean SUPPORTS_NORMAL_TERMINATION = true;
  70 
  71     private final int pid;
  72     private final ProcessHandleImpl processHandle;
  73     private int exitcode;
  74     private boolean hasExited;
  75 
  76     private /* final */ OutputStream stdin;
  77     private /* final */ InputStream  stdout;
  78     private /* final */ InputStream  stderr;
  79 



  80     private static enum LaunchMechanism {
  81         // order IS important!
  82         FORK,
  83         POSIX_SPAWN,
  84         VFORK
  85     }
  86 
  87     private static enum Platform {
  88 
  89         LINUX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.VFORK, LaunchMechanism.FORK),
  90 
  91         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  92 


  93         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  94 
  95         final LaunchMechanism defaultLaunchMechanism;
  96         final Set<LaunchMechanism> validLaunchMechanisms;
  97 
  98         Platform(LaunchMechanism ... launchMechanisms) {
  99             this.defaultLaunchMechanism = launchMechanisms[0];
 100             this.validLaunchMechanisms =
 101                 EnumSet.copyOf(Arrays.asList(launchMechanisms));
 102         }
 103 
 104         LaunchMechanism launchMechanism() {
 105             return AccessController.doPrivileged(
 106                 (PrivilegedAction<LaunchMechanism>) () -> {
 107                     String s = System.getProperty(
 108                         "jdk.lang.Process.launchMechanism");
 109                     LaunchMechanism lm;
 110                     if (s == null) {
 111                         lm = defaultLaunchMechanism;
 112                         s = lm.name().toLowerCase(Locale.ENGLISH);


 117                         } catch (IllegalArgumentException e) {
 118                             lm = null;
 119                         }
 120                     }
 121                     if (lm == null || !validLaunchMechanisms.contains(lm)) {
 122                         throw new Error(
 123                             s + " is not a supported " +
 124                             "process launch mechanism on this platform."
 125                         );
 126                     }
 127                     return lm;
 128                 }
 129             );
 130         }
 131 
 132         static Platform get() {
 133             String osName = GetPropertyAction.privilegedGetProperty("os.name");
 134 
 135             if (osName.equals("Linux")) { return LINUX; }
 136             if (osName.contains("OS X")) { return BSD; }

 137             if (osName.equals("AIX")) { return AIX; }
 138 
 139             throw new Error(osName + " is not a supported OS platform.");
 140         }
 141     }
 142 
 143     private static final Platform platform = Platform.get();
 144     private static final LaunchMechanism launchMechanism = platform.launchMechanism();
 145     private static final byte[] helperpath = toCString(StaticProperty.javaHome() + "/lib/jspawnhelper");
 146 
 147     private static byte[] toCString(String s) {
 148         if (s == null)
 149             return null;
 150         byte[] bytes = s.getBytes();
 151         byte[] result = new byte[bytes.length + 1];
 152         System.arraycopy(bytes, 0,
 153                          result, 0,
 154                          bytes.length);
 155         result[result.length-1] = (byte)0;
 156         return result;


 362                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 363                     synchronized (this) {
 364                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 365                         this.hasExited = true;
 366                         this.notifyAll();
 367                     }
 368 
 369                     if (stdout instanceof ProcessPipeInputStream)
 370                         ((ProcessPipeInputStream) stdout).processExited();
 371 
 372                     if (stderr instanceof ProcessPipeInputStream)
 373                         ((ProcessPipeInputStream) stderr).processExited();
 374 
 375                     if (stdin instanceof ProcessPipeOutputStream)
 376                         ((ProcessPipeOutputStream) stdin).processExited();
 377 
 378                     return null;
 379                 });
 380                 break;
 381 



































 382             case AIX:
 383                 stdin = (fds[0] == -1) ?
 384                         ProcessBuilder.NullOutputStream.INSTANCE :
 385                         new ProcessPipeOutputStream(fds[0]);
 386 
 387                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
 388                          ProcessBuilder.NullInputStream.INSTANCE :
 389                          new DeferredCloseProcessPipeInputStream(fds[1]);
 390 
 391                 stderr = (fds[2] == -1) ?
 392                          ProcessBuilder.NullInputStream.INSTANCE :
 393                          new DeferredCloseProcessPipeInputStream(fds[2]);
 394 
 395                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
 396                     synchronized (this) {
 397                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
 398                         this.hasExited = true;
 399                         this.notifyAll();
 400                     }
 401 


 462     }
 463 
 464     private void destroy(boolean force) {
 465         switch (platform) {
 466             case LINUX:
 467             case BSD:
 468             case AIX:
 469                 // There is a risk that pid will be recycled, causing us to
 470                 // kill the wrong process!  So we only terminate processes
 471                 // that appear to still be running.  Even with this check,
 472                 // there is an unavoidable race condition here, but the window
 473                 // is very small, and OSes try hard to not recycle pids too
 474                 // soon, so this is quite safe.
 475                 synchronized (this) {
 476                     if (!hasExited)
 477                         processHandle.destroyProcess(force);
 478                 }
 479                 try { stdin.close();  } catch (IOException ignored) {}
 480                 try { stdout.close(); } catch (IOException ignored) {}
 481                 try { stderr.close(); } catch (IOException ignored) {}























 482                 break;
 483 
 484             default: throw new AssertionError("Unsupported platform: " + platform);
 485         }
 486     }
 487 
 488     @Override
 489     public CompletableFuture<Process> onExit() {
 490         return ProcessHandleImpl.completion(pid, false)
 491                 .handleAsync((unusedExitStatus, unusedThrowable) -> {
 492                     boolean interrupted = false;
 493                     while (true) {
 494                         // Ensure that the concurrent task setting the exit status has completed
 495                         try {
 496                             waitFor();
 497                             break;
 498                         } catch (InterruptedException ie) {
 499                             interrupted = true;
 500                         }
 501                     }


< prev index next >