1 /* 2 * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang; 27 28 import java.lang.ProcessBuilder.Redirect; 29 import java.io.BufferedInputStream; 30 import java.io.BufferedOutputStream; 31 import java.io.ByteArrayInputStream; 32 import java.io.FileDescriptor; 33 import java.io.FileInputStream; 34 import java.io.FileOutputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.nio.charset.Charset; 39 import java.util.Arrays; 40 import java.util.Locale; 41 import java.util.concurrent.CompletableFuture; 42 import java.util.concurrent.TimeUnit; 43 import java.util.concurrent.locks.Condition; 44 import java.util.concurrent.locks.ReentrantLock; 45 import java.security.AccessController; 46 import java.security.PrivilegedActionException; 47 import java.security.PrivilegedExceptionAction; 48 import jdk.internal.access.JavaIOFileDescriptorAccess; 49 import jdk.internal.access.SharedSecrets; 50 import jdk.internal.util.OperatingSystem; 51 import jdk.internal.util.StaticProperty; 52 import sun.security.action.GetPropertyAction; 53 54 /** 55 * java.lang.Process subclass in the UNIX environment. 56 * 57 * @author Mario Wolczko and Ross Knippel. 58 * @author Konstantin Kladko (ported to Linux and Bsd) 59 * @author Martin Buchholz 60 * @author Volker Simonis (ported to AIX) 61 * @since 1.5 62 */ 63 final class ProcessImpl extends Process { 64 private static final JavaIOFileDescriptorAccess fdAccess 65 = SharedSecrets.getJavaIOFileDescriptorAccess(); 66 67 // Linux platforms support a normal (non-forcible) kill signal. 68 static final boolean SUPPORTS_NORMAL_TERMINATION = true; 69 70 // Cache for JNU Charset. The encoding name is guaranteed 71 // to be supported in this environment. 72 static final Charset JNU_CHARSET = Charset.forName(StaticProperty.jnuEncoding()); 73 74 private final int pid; 75 private final ProcessHandleImpl processHandle; 76 private int exitcode; 77 private boolean hasExited; 78 79 private final ReentrantLock lock = new ReentrantLock(); 80 private final Condition condition = lock.newCondition(); 81 82 private /* final */ OutputStream stdin; 83 private /* final */ InputStream stdout; 84 private /* final */ InputStream stderr; 85 86 private static enum LaunchMechanism { 87 // order IS important! 88 FORK, 89 POSIX_SPAWN, 90 VFORK 91 } 92 93 /** 94 * {@return the default or requested launch mechanism} 95 * @throws Error if the requested launch mechanism is not found or valid 96 */ 97 private static LaunchMechanism launchMechanism() { 98 String s = GetPropertyAction.privilegedGetProperty("jdk.lang.Process.launchMechanism"); 99 if (s == null) { 100 return LaunchMechanism.POSIX_SPAWN; 101 } 102 103 try { 104 // Should be value of a LaunchMechanism enum 105 LaunchMechanism lm = LaunchMechanism.valueOf(s.toUpperCase(Locale.ROOT)); 106 switch (OperatingSystem.current()) { 107 case LINUX: 108 return lm; // All options are valid for Linux 109 case AIX: 110 case MACOS: 111 if (lm != LaunchMechanism.VFORK) { 112 return lm; // All but VFORK are valid 113 } 114 break; 115 case WINDOWS: 116 // fall through to throw to Error 117 } 118 } catch (IllegalArgumentException e) { 119 } 120 121 throw new Error(s + " is not a supported " + 122 "process launch mechanism on this platform: " + OperatingSystem.current()); 123 } 124 125 private static final LaunchMechanism launchMechanism = launchMechanism(); 126 private static final byte[] helperpath = toCString(StaticProperty.javaHome() + "/lib/jspawnhelper"); 127 128 private static byte[] toCString(String s) { 129 if (s == null) 130 return null; 131 byte[] bytes = s.getBytes(JNU_CHARSET); 132 byte[] result = new byte[bytes.length + 1]; 133 System.arraycopy(bytes, 0, 134 result, 0, 135 bytes.length); 136 result[result.length-1] = (byte)0; 137 return result; 138 } 139 140 // Only for use by ProcessBuilder.start() 141 static Process start(String[] cmdarray, 142 java.util.Map<String,String> environment, 143 String dir, 144 ProcessBuilder.Redirect[] redirects, 145 boolean redirectErrorStream) 146 throws IOException 147 { 148 assert cmdarray != null && cmdarray.length > 0; 149 150 // Convert arguments to a contiguous block; it's easier to do 151 // memory management in Java than in C. 152 byte[][] args = new byte[cmdarray.length-1][]; 153 int size = args.length; // For added NUL bytes 154 for (int i = 0; i < args.length; i++) { 155 args[i] = cmdarray[i+1].getBytes(JNU_CHARSET); 156 size += args[i].length; 157 } 158 byte[] argBlock = new byte[size]; 159 int i = 0; 160 for (byte[] arg : args) { 161 System.arraycopy(arg, 0, argBlock, i, arg.length); 162 i += arg.length + 1; 163 // No need to write NUL bytes explicitly 164 } 165 166 int[] envc = new int[1]; 167 byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc); 168 169 int[] std_fds; 170 171 FileInputStream f0 = null; 172 FileOutputStream f1 = null; 173 FileOutputStream f2 = null; 174 175 try { 176 boolean forceNullOutputStream = false; 177 if (redirects == null) { 178 std_fds = new int[] { -1, -1, -1 }; 179 } else { 180 std_fds = new int[3]; 181 182 if (redirects[0] == Redirect.PIPE) { 183 std_fds[0] = -1; 184 } else if (redirects[0] == Redirect.INHERIT) { 185 std_fds[0] = 0; 186 } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { 187 std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd()); 188 } else { 189 f0 = new FileInputStream(redirects[0].file()); 190 std_fds[0] = fdAccess.get(f0.getFD()); 191 } 192 193 if (redirects[1] == Redirect.PIPE) { 194 std_fds[1] = -1; 195 } else if (redirects[1] == Redirect.INHERIT) { 196 std_fds[1] = 1; 197 } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { 198 std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd()); 199 // Force getInputStream to return a null stream, 200 // the fd is directly assigned to the next process. 201 forceNullOutputStream = true; 202 } else { 203 f1 = new FileOutputStream(redirects[1].file(), 204 redirects[1].append()); 205 std_fds[1] = fdAccess.get(f1.getFD()); 206 } 207 208 if (redirects[2] == Redirect.PIPE) { 209 std_fds[2] = -1; 210 } else if (redirects[2] == Redirect.INHERIT) { 211 std_fds[2] = 2; 212 } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { 213 std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd()); 214 } else { 215 f2 = new FileOutputStream(redirects[2].file(), 216 redirects[2].append()); 217 std_fds[2] = fdAccess.get(f2.getFD()); 218 } 219 } 220 221 Process p = new ProcessImpl 222 (toCString(cmdarray[0]), 223 argBlock, args.length, 224 envBlock, envc[0], 225 toCString(dir), 226 std_fds, 227 forceNullOutputStream, 228 redirectErrorStream); 229 if (redirects != null) { 230 // Copy the fd's if they are to be redirected to another process 231 if (std_fds[0] >= 0 && 232 redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { 233 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]); 234 } 235 if (std_fds[1] >= 0 && 236 redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { 237 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]); 238 } 239 if (std_fds[2] >= 0 && 240 redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { 241 fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]); 242 } 243 } 244 return p; 245 } finally { 246 // In theory, close() can throw IOException 247 // (although it is rather unlikely to happen here) 248 try { if (f0 != null) f0.close(); } 249 finally { 250 try { if (f1 != null) f1.close(); } 251 finally { if (f2 != null) f2.close(); } 252 } 253 } 254 } 255 256 257 /** 258 * Creates a process. Depending on the {@code mode} flag, this is done by 259 * one of the following mechanisms: 260 * <pre> 261 * 1 - fork(2) and exec(2) 262 * 2 - posix_spawn(3P) 263 * 3 - vfork(2) and exec(2) 264 * </pre> 265 * @param fds an array of three file descriptors. 266 * Indexes 0, 1, and 2 correspond to standard input, 267 * standard output and standard error, respectively. On 268 * input, a value of -1 means to create a pipe to connect 269 * child and parent processes. On output, a value which 270 * is not -1 is the parent pipe fd corresponding to the 271 * pipe which has been created. An element of this array 272 * is -1 on input if and only if it is <em>not</em> -1 on 273 * output. 274 * @return the pid of the subprocess 275 */ 276 private native int forkAndExec(int mode, byte[] helperpath, 277 byte[] prog, 278 byte[] argBlock, int argc, 279 byte[] envBlock, int envc, 280 byte[] dir, 281 int[] fds, 282 boolean redirectErrorStream) 283 throws IOException; 284 285 @SuppressWarnings("removal") 286 private ProcessImpl(final byte[] prog, 287 final byte[] argBlock, final int argc, 288 final byte[] envBlock, final int envc, 289 final byte[] dir, 290 final int[] fds, 291 final boolean forceNullOutputStream, 292 final boolean redirectErrorStream) 293 throws IOException { 294 295 pid = forkAndExec(launchMechanism.ordinal() + 1, 296 helperpath, 297 prog, 298 argBlock, argc, 299 envBlock, envc, 300 dir, 301 fds, 302 redirectErrorStream); 303 processHandle = ProcessHandleImpl.getInternal(pid); 304 305 try { 306 AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> { 307 initStreams(fds, forceNullOutputStream); 308 return null; 309 }); 310 } catch (PrivilegedActionException ex) { 311 throw (IOException) ex.getCause(); 312 } 313 } 314 315 static FileDescriptor newFileDescriptor(int fd) { 316 FileDescriptor fileDescriptor = new FileDescriptor(); 317 fdAccess.set(fileDescriptor, fd); 318 return fileDescriptor; 319 } 320 321 /** 322 * Initialize the streams from the file descriptors. 323 * @param fds array of stdin, stdout, stderr fds 324 * @param forceNullOutputStream true if the stdout is being directed to 325 * a subsequent process. The stdout stream should be a null output stream . 326 * @throws IOException 327 */ 328 void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException { 329 switch (OperatingSystem.current()) { 330 case LINUX: 331 case MACOS: 332 stdin = (fds[0] == -1) ? 333 ProcessBuilder.NullOutputStream.INSTANCE : 334 new ProcessPipeOutputStream(fds[0]); 335 336 stdout = (fds[1] == -1 || forceNullOutputStream) ? 337 ProcessBuilder.NullInputStream.INSTANCE : 338 new ProcessPipeInputStream(fds[1]); 339 340 stderr = (fds[2] == -1) ? 341 ProcessBuilder.NullInputStream.INSTANCE : 342 new ProcessPipeInputStream(fds[2]); 343 344 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> { 345 lock.lock(); 346 try { 347 this.exitcode = (exitcode == null) ? -1 : exitcode.intValue(); 348 this.hasExited = true; 349 condition.signalAll(); 350 } finally { 351 lock.unlock(); 352 } 353 354 if (stdout instanceof ProcessPipeInputStream) 355 ((ProcessPipeInputStream) stdout).processExited(); 356 357 if (stderr instanceof ProcessPipeInputStream) 358 ((ProcessPipeInputStream) stderr).processExited(); 359 360 if (stdin instanceof ProcessPipeOutputStream) 361 ((ProcessPipeOutputStream) stdin).processExited(); 362 363 return null; 364 }); 365 break; 366 367 case AIX: 368 stdin = (fds[0] == -1) ? 369 ProcessBuilder.NullOutputStream.INSTANCE : 370 new ProcessPipeOutputStream(fds[0]); 371 372 stdout = (fds[1] == -1 || forceNullOutputStream) ? 373 ProcessBuilder.NullInputStream.INSTANCE : 374 new DeferredCloseProcessPipeInputStream(fds[1]); 375 376 stderr = (fds[2] == -1) ? 377 ProcessBuilder.NullInputStream.INSTANCE : 378 new DeferredCloseProcessPipeInputStream(fds[2]); 379 380 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> { 381 lock.lock(); 382 try { 383 this.exitcode = (exitcode == null) ? -1 : exitcode.intValue(); 384 this.hasExited = true; 385 condition.signalAll(); 386 } finally { 387 lock.unlock(); 388 } 389 390 if (stdout instanceof DeferredCloseProcessPipeInputStream) 391 ((DeferredCloseProcessPipeInputStream) stdout).processExited(); 392 393 if (stderr instanceof DeferredCloseProcessPipeInputStream) 394 ((DeferredCloseProcessPipeInputStream) stderr).processExited(); 395 396 if (stdin instanceof ProcessPipeOutputStream) 397 ((ProcessPipeOutputStream) stdin).processExited(); 398 399 return null; 400 }); 401 break; 402 403 default: 404 throw new AssertionError("Unsupported platform: " + 405 OperatingSystem.current()); 406 } 407 } 408 409 public OutputStream getOutputStream() { 410 return stdin; 411 } 412 413 public InputStream getInputStream() { 414 return stdout; 415 } 416 417 public InputStream getErrorStream() { 418 return stderr; 419 } 420 421 public int waitFor() throws InterruptedException { 422 lock.lock(); 423 try { 424 while (!hasExited) { 425 condition.await(); 426 } 427 return exitcode; 428 } finally { 429 lock.unlock(); 430 } 431 } 432 433 public boolean waitFor(long timeout, TimeUnit unit) 434 throws InterruptedException 435 { 436 lock.lock(); 437 try { 438 long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions 439 while (remainingNanos > 0 && !hasExited) { 440 remainingNanos = condition.awaitNanos(remainingNanos); 441 } 442 return hasExited; 443 } finally { 444 lock.unlock(); 445 } 446 } 447 448 public int exitValue() { 449 lock.lock(); 450 try { 451 if (!hasExited) { 452 throw new IllegalThreadStateException("process hasn't exited"); 453 } 454 return exitcode; 455 } finally { 456 lock.unlock(); 457 } 458 } 459 460 private void destroy(boolean force) { 461 switch (OperatingSystem.current()) { 462 case LINUX: 463 case MACOS: 464 case AIX: 465 // There is a risk that pid will be recycled, causing us to 466 // kill the wrong process! So we only terminate processes 467 // that appear to still be running. Even with this check, 468 // there is an unavoidable race condition here, but the window 469 // is very small, and OSes try hard to not recycle pids too 470 // soon, so this is quite safe. 471 lock.lock(); 472 try { 473 if (!hasExited) 474 processHandle.destroyProcess(force); 475 } finally { 476 lock.unlock(); 477 } 478 try { stdin.close(); } catch (IOException ignored) {} 479 try { stdout.close(); } catch (IOException ignored) {} 480 try { stderr.close(); } catch (IOException ignored) {} 481 break; 482 483 default: throw new AssertionError("Unsupported platform: " + OperatingSystem.current()); 484 } 485 } 486 487 @Override 488 public CompletableFuture<Process> onExit() { 489 return ProcessHandleImpl.completion(pid, false) 490 .handleAsync((unusedExitStatus, unusedThrowable) -> { 491 boolean interrupted = false; 492 while (true) { 493 // Ensure that the concurrent task setting the exit status has completed 494 try { 495 waitFor(); 496 break; 497 } catch (InterruptedException ie) { 498 interrupted = true; 499 } 500 } 501 if (interrupted) { 502 Thread.currentThread().interrupt(); 503 } 504 return this; 505 }); 506 } 507 508 @Override 509 public ProcessHandle toHandle() { 510 @SuppressWarnings("removal") 511 SecurityManager sm = System.getSecurityManager(); 512 if (sm != null) { 513 sm.checkPermission(new RuntimePermission("manageProcess")); 514 } 515 return processHandle; 516 } 517 518 @Override 519 public boolean supportsNormalTermination() { 520 return ProcessImpl.SUPPORTS_NORMAL_TERMINATION; 521 } 522 523 @Override 524 public void destroy() { 525 destroy(false); 526 } 527 528 @Override 529 public Process destroyForcibly() { 530 destroy(true); 531 return this; 532 } 533 534 @Override 535 public long pid() { 536 return pid; 537 } 538 539 @Override 540 public boolean isAlive() { 541 lock.lock(); 542 try { 543 return !hasExited; 544 } finally { 545 lock.unlock(); 546 } 547 } 548 549 /** 550 * The {@code toString} method returns a string consisting of 551 * the native process ID of the process and the exit value of the process. 552 * 553 * @return a string representation of the object. 554 */ 555 @Override 556 public String toString() { 557 return new StringBuilder("Process[pid=").append(pid) 558 .append(", exitValue=").append(hasExited ? exitcode : "\"not exited\"") 559 .append("]").toString(); 560 } 561 562 private static native void init(); 563 564 static { 565 init(); 566 } 567 568 /** 569 * A buffered input stream for a subprocess pipe file descriptor 570 * that allows the underlying file descriptor to be reclaimed when 571 * the process exits, via the processExited hook. 572 * 573 * This is tricky because we do not want the user-level InputStream to be 574 * closed until the user invokes close(), and we need to continue to be 575 * able to read any buffered data lingering in the OS pipe buffer. 576 */ 577 private static class ProcessPipeInputStream extends BufferedInputStream { 578 private final Object closeLock = new Object(); 579 580 ProcessPipeInputStream(int fd) { 581 super(new PipeInputStream(newFileDescriptor(fd))); 582 } 583 private static byte[] drainInputStream(InputStream in) 584 throws IOException { 585 int n = 0; 586 int j; 587 byte[] a = null; 588 while ((j = in.available()) > 0) { 589 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 590 n += in.read(a, n, j); 591 } 592 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); 593 } 594 595 /** Called by the process reaper thread when the process exits. */ 596 synchronized void processExited() { 597 synchronized (closeLock) { 598 try { 599 InputStream in = this.in; 600 // this stream is closed if and only if: in == null 601 if (in != null) { 602 byte[] stragglers = drainInputStream(in); 603 in.close(); 604 this.in = (stragglers == null) ? 605 ProcessBuilder.NullInputStream.INSTANCE : 606 new ByteArrayInputStream(stragglers); 607 } 608 } catch (IOException ignored) {} 609 } 610 } 611 612 @Override 613 public void close() throws IOException { 614 // BufferedInputStream#close() is not synchronized unlike most other 615 // methods. Synchronizing helps avoid race with processExited(). 616 synchronized (closeLock) { 617 super.close(); 618 } 619 } 620 } 621 622 /** 623 * A buffered output stream for a subprocess pipe file descriptor 624 * that allows the underlying file descriptor to be reclaimed when 625 * the process exits, via the processExited hook. 626 */ 627 private static class ProcessPipeOutputStream extends BufferedOutputStream { 628 ProcessPipeOutputStream(int fd) { 629 super(new PipeOutputStream(newFileDescriptor(fd))); 630 } 631 632 /** Called by the process reaper thread when the process exits. */ 633 synchronized void processExited() { 634 OutputStream out = this.out; 635 if (out != null) { 636 try { 637 out.close(); 638 } catch (IOException ignored) { 639 // We know of no reason to get an IOException, but if 640 // we do, there's nothing else to do but carry on. 641 } 642 this.out = ProcessBuilder.NullOutputStream.INSTANCE; 643 } 644 } 645 } 646 647 /** 648 * A buffered input stream for a subprocess pipe file descriptor 649 * that allows the underlying file descriptor to be reclaimed when 650 * the process exits, via the processExited hook. 651 * 652 * This is tricky because we do not want the user-level InputStream to be 653 * closed until the user invokes close(), and we need to continue to be 654 * able to read any buffered data lingering in the OS pipe buffer. 655 * 656 * On AIX this is especially tricky, because the 'close()' system call 657 * will block if another thread is at the same time blocked in a file 658 * operation (e.g. 'read()') on the same file descriptor. We therefore 659 * combine 'ProcessPipeInputStream' approach used on Linux and Bsd 660 * with the deferring 'close' of InputStream. This means 661 * that every potentially blocking operation on the file descriptor 662 * increments a counter before it is executed and decrements it once it 663 * finishes. The 'close()' operation will only be executed if there are 664 * no pending operations. Otherwise it is deferred after the last pending 665 * operation has finished. 666 * 667 */ 668 private static class DeferredCloseProcessPipeInputStream 669 extends BufferedInputStream { 670 671 private final Object closeLock = new Object(); 672 private int useCount = 0; 673 private boolean closePending = false; 674 675 DeferredCloseProcessPipeInputStream(int fd) { 676 super(new PipeInputStream(newFileDescriptor(fd))); 677 } 678 679 private InputStream drainInputStream(InputStream in) 680 throws IOException { 681 int n = 0; 682 int j; 683 byte[] a = null; 684 synchronized (closeLock) { 685 if (buf == null) // asynchronous close()? 686 return null; // discard 687 j = in.available(); 688 } 689 while (j > 0) { 690 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 691 synchronized (closeLock) { 692 if (buf == null) // asynchronous close()? 693 return null; // discard 694 n += in.read(a, n, j); 695 j = in.available(); 696 } 697 } 698 return (a == null) ? 699 ProcessBuilder.NullInputStream.INSTANCE : 700 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); 701 } 702 703 /** Called by the process reaper thread when the process exits. */ 704 synchronized void processExited() { 705 try { 706 InputStream in = this.in; 707 if (in != null) { 708 InputStream stragglers = drainInputStream(in); 709 in.close(); 710 this.in = stragglers; 711 } 712 } catch (IOException ignored) { } 713 } 714 715 private void raise() { 716 synchronized (closeLock) { 717 useCount++; 718 } 719 } 720 721 private void lower() throws IOException { 722 synchronized (closeLock) { 723 useCount--; 724 if (useCount == 0 && closePending) { 725 closePending = false; 726 super.close(); 727 } 728 } 729 } 730 731 @Override 732 public int read() throws IOException { 733 raise(); 734 try { 735 return super.read(); 736 } finally { 737 lower(); 738 } 739 } 740 741 @Override 742 public int read(byte[] b) throws IOException { 743 raise(); 744 try { 745 return super.read(b); 746 } finally { 747 lower(); 748 } 749 } 750 751 @Override 752 public int read(byte[] b, int off, int len) throws IOException { 753 raise(); 754 try { 755 return super.read(b, off, len); 756 } finally { 757 lower(); 758 } 759 } 760 761 @Override 762 public long skip(long n) throws IOException { 763 raise(); 764 try { 765 return super.skip(n); 766 } finally { 767 lower(); 768 } 769 } 770 771 @Override 772 public int available() throws IOException { 773 raise(); 774 try { 775 return super.available(); 776 } finally { 777 lower(); 778 } 779 } 780 781 @Override 782 public void close() throws IOException { 783 // BufferedInputStream#close() is not synchronized unlike most other 784 // methods. Synchronizing helps avoid racing with drainInputStream(). 785 synchronized (closeLock) { 786 if (useCount == 0) { 787 super.close(); 788 } 789 else { 790 closePending = true; 791 } 792 } 793 } 794 } 795 }