1 /* 2 * Copyright (c) 1995, 2014, 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.io.BufferedInputStream; 29 import java.io.BufferedOutputStream; 30 import java.io.ByteArrayInputStream; 31 import java.io.FileDescriptor; 32 import java.io.FileInputStream; 33 import java.io.FileOutputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.util.Arrays; 38 import java.util.EnumSet; 39 import java.util.Locale; 40 import java.util.Set; 41 import java.util.concurrent.Executors; 42 import java.util.concurrent.Executor; 43 import java.util.concurrent.ThreadFactory; 44 import java.util.concurrent.TimeUnit; 45 import java.security.AccessController; 46 import static java.security.AccessController.doPrivileged; 47 import java.security.PrivilegedAction; 48 import java.security.PrivilegedActionException; 49 import java.security.PrivilegedExceptionAction; 50 51 /** 52 * java.lang.Process subclass in the UNIX environment. 53 * 54 * @author Mario Wolczko and Ross Knippel. 55 * @author Konstantin Kladko (ported to Linux and Bsd) 56 * @author Martin Buchholz 57 * @author Volker Simonis (ported to AIX) 58 */ 59 final class UNIXProcess extends Process { 60 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess 61 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); 62 63 private final int pid; 64 private int exitcode; 65 private boolean hasExited; 66 67 private /* final */ OutputStream stdin; 68 private /* final */ InputStream stdout; 69 private /* final */ InputStream stderr; 70 71 // only used on Solaris 72 private /* final */ DeferredCloseInputStream stdout_inner_stream; 73 74 private static enum LaunchMechanism { 75 // order IS important! 76 FORK, 77 POSIX_SPAWN, 78 VFORK 79 } 80 81 private static enum Platform { 82 83 LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK), 84 85 BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 86 87 SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), 88 89 AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); 90 91 final LaunchMechanism defaultLaunchMechanism; 92 final Set<LaunchMechanism> validLaunchMechanisms; 93 94 Platform(LaunchMechanism ... launchMechanisms) { 95 this.defaultLaunchMechanism = launchMechanisms[0]; 96 this.validLaunchMechanisms = 97 EnumSet.copyOf(Arrays.asList(launchMechanisms)); 98 } 99 100 @SuppressWarnings("fallthrough") 101 private String helperPath(String javahome, String osArch) { 102 switch (this) { 103 case SOLARIS: 104 if (osArch.equals("x86")) { osArch = "i386"; } 105 else if (osArch.equals("x86_64")) { osArch = "amd64"; } 106 // fall through... 107 case LINUX: 108 case AIX: 109 return javahome + "/lib/" + osArch + "/jspawnhelper"; 110 111 case BSD: 112 return javahome + "/lib/jspawnhelper"; 113 114 default: 115 throw new AssertionError("Unsupported platform: " + this); 116 } 117 } 118 119 String helperPath() { 120 return AccessController.doPrivileged( 121 (PrivilegedAction<String>) () -> 122 helperPath(System.getProperty("java.home"), 123 System.getProperty("os.arch")) 124 ); 125 } 126 127 LaunchMechanism launchMechanism() { 128 return AccessController.doPrivileged( 129 (PrivilegedAction<LaunchMechanism>) () -> { 130 String s = System.getProperty( 131 "jdk.lang.Process.launchMechanism"); 132 LaunchMechanism lm; 133 if (s == null) { 134 lm = defaultLaunchMechanism; 135 s = lm.name().toLowerCase(Locale.ENGLISH); 136 } else { 137 try { 138 lm = LaunchMechanism.valueOf( 139 s.toUpperCase(Locale.ENGLISH)); 140 } catch (IllegalArgumentException e) { 141 lm = null; 142 } 143 } 144 if (lm == null || !validLaunchMechanisms.contains(lm)) { 145 throw new Error( 146 s + " is not a supported " + 147 "process launch mechanism on this platform." 148 ); 149 } 150 return lm; 151 } 152 ); 153 } 154 155 static Platform get() { 156 String osName = AccessController.doPrivileged( 157 (PrivilegedAction<String>) () -> System.getProperty("os.name") 158 ); 159 160 if (osName.equals("Linux")) { return LINUX; } 161 if (osName.contains("OS X")) { return BSD; } 162 if (osName.equals("SunOS")) { return SOLARIS; } 163 if (osName.equals("AIX")) { return AIX; } 164 165 throw new Error(osName + " is not a supported OS platform."); 166 } 167 } 168 169 private static final Platform platform = Platform.get(); 170 private static final LaunchMechanism launchMechanism = platform.launchMechanism(); 171 private static final byte[] helperpath = toCString(platform.helperPath()); 172 173 private static byte[] toCString(String s) { 174 if (s == null) 175 return null; 176 byte[] bytes = s.getBytes(); 177 byte[] result = new byte[bytes.length + 1]; 178 System.arraycopy(bytes, 0, 179 result, 0, 180 bytes.length); 181 result[result.length-1] = (byte)0; 182 return result; 183 } 184 185 /* this is for the reaping thread */ 186 private native int waitForProcessExit(int pid); 187 188 /** 189 * Creates a process. Depending on the {@code mode} flag, this is done by 190 * one of the following mechanisms: 191 * <pre> 192 * 1 - fork(2) and exec(2) 193 * 2 - posix_spawn(3P) 194 * 3 - vfork(2) and exec(2) 195 * 196 * (4 - clone(2) and exec(2) - obsolete and currently disabled in native code) 197 * </pre> 198 * @param fds an array of three file descriptors. 199 * Indexes 0, 1, and 2 correspond to standard input, 200 * standard output and standard error, respectively. On 201 * input, a value of -1 means to create a pipe to connect 202 * child and parent processes. On output, a value which 203 * is not -1 is the parent pipe fd corresponding to the 204 * pipe which has been created. An element of this array 205 * is -1 on input if and only if it is <em>not</em> -1 on 206 * output. 207 * @return the pid of the subprocess 208 */ 209 private native int forkAndExec(int mode, byte[] helperpath, 210 byte[] prog, 211 byte[] argBlock, int argc, 212 byte[] envBlock, int envc, 213 byte[] dir, 214 int[] fds, 215 boolean redirectErrorStream) 216 throws IOException; 217 218 /** 219 * The thread pool of "process reaper" daemon threads. 220 */ 221 private static final Executor processReaperExecutor = 222 doPrivileged((PrivilegedAction<Executor>) () -> { 223 224 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 225 while (tg.getParent() != null) tg = tg.getParent(); 226 ThreadGroup systemThreadGroup = tg; 227 228 ThreadFactory threadFactory = grimReaper -> { 229 // Our thread stack requirement is quite modest. 230 Thread t = new Thread(systemThreadGroup, grimReaper, 231 "process reaper", 65536); 232 t.setDaemon(true); 233 // A small attempt (probably futile) to avoid priority inversion 234 t.setPriority(Thread.MAX_PRIORITY); 235 return t; 236 }; 237 238 return Executors.newCachedThreadPool(threadFactory); 239 }); 240 241 UNIXProcess(final byte[] prog, 242 final byte[] argBlock, final int argc, 243 final byte[] envBlock, final int envc, 244 final byte[] dir, 245 final int[] fds, 246 final boolean redirectErrorStream) 247 throws IOException { 248 249 pid = forkAndExec(launchMechanism.ordinal() + 1, 250 helperpath, 251 prog, 252 argBlock, argc, 253 envBlock, envc, 254 dir, 255 fds, 256 redirectErrorStream); 257 258 try { 259 doPrivileged((PrivilegedExceptionAction<Void>) () -> { 260 initStreams(fds); 261 return null; 262 }); 263 } catch (PrivilegedActionException ex) { 264 throw (IOException) ex.getException(); 265 } 266 } 267 268 static FileDescriptor newFileDescriptor(int fd) { 269 FileDescriptor fileDescriptor = new FileDescriptor(); 270 fdAccess.set(fileDescriptor, fd); 271 return fileDescriptor; 272 } 273 274 void initStreams(int[] fds) throws IOException { 275 switch (platform) { 276 case LINUX: 277 case BSD: 278 stdin = (fds[0] == -1) ? 279 ProcessBuilder.NullOutputStream.INSTANCE : 280 new ProcessPipeOutputStream(fds[0]); 281 282 stdout = (fds[1] == -1) ? 283 ProcessBuilder.NullInputStream.INSTANCE : 284 new ProcessPipeInputStream(fds[1]); 285 286 stderr = (fds[2] == -1) ? 287 ProcessBuilder.NullInputStream.INSTANCE : 288 new ProcessPipeInputStream(fds[2]); 289 290 processReaperExecutor.execute(() -> { 291 int exitcode = waitForProcessExit(pid); 292 293 synchronized (this) { 294 this.exitcode = exitcode; 295 this.hasExited = true; 296 this.notifyAll(); 297 } 298 299 if (stdout instanceof ProcessPipeInputStream) 300 ((ProcessPipeInputStream) stdout).processExited(); 301 302 if (stderr instanceof ProcessPipeInputStream) 303 ((ProcessPipeInputStream) stderr).processExited(); 304 305 if (stdin instanceof ProcessPipeOutputStream) 306 ((ProcessPipeOutputStream) stdin).processExited(); 307 }); 308 break; 309 310 case SOLARIS: 311 stdin = (fds[0] == -1) ? 312 ProcessBuilder.NullOutputStream.INSTANCE : 313 new BufferedOutputStream( 314 new FileOutputStream(newFileDescriptor(fds[0]))); 315 316 stdout = (fds[1] == -1) ? 317 ProcessBuilder.NullInputStream.INSTANCE : 318 new BufferedInputStream( 319 stdout_inner_stream = 320 new DeferredCloseInputStream( 321 newFileDescriptor(fds[1]))); 322 323 stderr = (fds[2] == -1) ? 324 ProcessBuilder.NullInputStream.INSTANCE : 325 new DeferredCloseInputStream(newFileDescriptor(fds[2])); 326 327 /* 328 * For each subprocess forked a corresponding reaper task 329 * is submitted. That task is the only thread which waits 330 * for the subprocess to terminate and it doesn't hold any 331 * locks while doing so. This design allows waitFor() and 332 * exitStatus() to be safely executed in parallel (and they 333 * need no native code). 334 */ 335 processReaperExecutor.execute(() -> { 336 int exitcode = waitForProcessExit(pid); 337 338 synchronized (this) { 339 this.exitcode = exitcode; 340 this.hasExited = true; 341 this.notifyAll(); 342 } 343 }); 344 break; 345 346 case AIX: 347 stdin = (fds[0] == -1) ? 348 ProcessBuilder.NullOutputStream.INSTANCE : 349 new ProcessPipeOutputStream(fds[0]); 350 351 stdout = (fds[1] == -1) ? 352 ProcessBuilder.NullInputStream.INSTANCE : 353 new DeferredCloseProcessPipeInputStream(fds[1]); 354 355 stderr = (fds[2] == -1) ? 356 ProcessBuilder.NullInputStream.INSTANCE : 357 new DeferredCloseProcessPipeInputStream(fds[2]); 358 359 processReaperExecutor.execute(() -> { 360 int exitcode = waitForProcessExit(pid); 361 362 synchronized (this) { 363 this.exitcode = exitcode; 364 this.hasExited = true; 365 this.notifyAll(); 366 } 367 368 if (stdout instanceof DeferredCloseProcessPipeInputStream) 369 ((DeferredCloseProcessPipeInputStream) stdout).processExited(); 370 371 if (stderr instanceof DeferredCloseProcessPipeInputStream) 372 ((DeferredCloseProcessPipeInputStream) stderr).processExited(); 373 374 if (stdin instanceof ProcessPipeOutputStream) 375 ((ProcessPipeOutputStream) stdin).processExited(); 376 }); 377 break; 378 379 default: throw new AssertionError("Unsupported platform: " + platform); 380 } 381 } 382 383 public OutputStream getOutputStream() { 384 return stdin; 385 } 386 387 public InputStream getInputStream() { 388 return stdout; 389 } 390 391 public InputStream getErrorStream() { 392 return stderr; 393 } 394 395 public synchronized int waitFor() throws InterruptedException { 396 while (!hasExited) { 397 wait(); 398 } 399 return exitcode; 400 } 401 402 @Override 403 public synchronized boolean waitFor(long timeout, TimeUnit unit) 404 throws InterruptedException 405 { 406 if (hasExited) return true; 407 if (timeout <= 0) return false; 408 409 long timeoutAsNanos = unit.toNanos(timeout); 410 long startTime = System.nanoTime(); 411 long rem = timeoutAsNanos; 412 413 while (!hasExited && (rem > 0)) { 414 wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1)); 415 rem = timeoutAsNanos - (System.nanoTime() - startTime); 416 } 417 return hasExited; 418 } 419 420 public synchronized int exitValue() { 421 if (!hasExited) { 422 throw new IllegalThreadStateException("process hasn't exited"); 423 } 424 return exitcode; 425 } 426 427 private static native void destroyProcess(int pid, boolean force); 428 429 private void destroy(boolean force) { 430 switch (platform) { 431 case LINUX: 432 case BSD: 433 case AIX: 434 // There is a risk that pid will be recycled, causing us to 435 // kill the wrong process! So we only terminate processes 436 // that appear to still be running. Even with this check, 437 // there is an unavoidable race condition here, but the window 438 // is very small, and OSes try hard to not recycle pids too 439 // soon, so this is quite safe. 440 synchronized (this) { 441 if (!hasExited) 442 destroyProcess(pid, force); 443 } 444 try { stdin.close(); } catch (IOException ignored) {} 445 try { stdout.close(); } catch (IOException ignored) {} 446 try { stderr.close(); } catch (IOException ignored) {} 447 break; 448 449 case SOLARIS: 450 // There is a risk that pid will be recycled, causing us to 451 // kill the wrong process! So we only terminate processes 452 // that appear to still be running. Even with this check, 453 // there is an unavoidable race condition here, but the window 454 // is very small, and OSes try hard to not recycle pids too 455 // soon, so this is quite safe. 456 synchronized (this) { 457 if (!hasExited) 458 destroyProcess(pid, force); 459 try { 460 stdin.close(); 461 if (stdout_inner_stream != null) 462 stdout_inner_stream.closeDeferred(stdout); 463 if (stderr instanceof DeferredCloseInputStream) 464 ((DeferredCloseInputStream) stderr) 465 .closeDeferred(stderr); 466 } catch (IOException e) { 467 // ignore 468 } 469 } 470 break; 471 472 default: throw new AssertionError("Unsupported platform: " + platform); 473 } 474 } 475 476 public void destroy() { 477 destroy(false); 478 } 479 480 @Override 481 public Process destroyForcibly() { 482 destroy(true); 483 return this; 484 } 485 486 @Override 487 public long getPid() { 488 return pid; 489 } 490 491 @Override 492 public synchronized boolean isAlive() { 493 return !hasExited; 494 } 495 496 private static native void init(); 497 498 static { 499 init(); 500 } 501 502 /** 503 * A buffered input stream for a subprocess pipe file descriptor 504 * that allows the underlying file descriptor to be reclaimed when 505 * the process exits, via the processExited hook. 506 * 507 * This is tricky because we do not want the user-level InputStream to be 508 * closed until the user invokes close(), and we need to continue to be 509 * able to read any buffered data lingering in the OS pipe buffer. 510 */ 511 private static class ProcessPipeInputStream extends BufferedInputStream { 512 private final Object closeLock = new Object(); 513 514 ProcessPipeInputStream(int fd) { 515 super(new FileInputStream(newFileDescriptor(fd))); 516 } 517 private static byte[] drainInputStream(InputStream in) 518 throws IOException { 519 int n = 0; 520 int j; 521 byte[] a = null; 522 while ((j = in.available()) > 0) { 523 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 524 n += in.read(a, n, j); 525 } 526 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); 527 } 528 529 /** Called by the process reaper thread when the process exits. */ 530 synchronized void processExited() { 531 synchronized (closeLock) { 532 try { 533 InputStream in = this.in; 534 // this stream is closed if and only if: in == null 535 if (in != null) { 536 byte[] stragglers = drainInputStream(in); 537 in.close(); 538 this.in = (stragglers == null) ? 539 ProcessBuilder.NullInputStream.INSTANCE : 540 new ByteArrayInputStream(stragglers); 541 } 542 } catch (IOException ignored) {} 543 } 544 } 545 546 @Override 547 public void close() throws IOException { 548 // BufferedInputStream#close() is not synchronized unlike most other 549 // methods. Synchronizing helps avoid race with processExited(). 550 synchronized (closeLock) { 551 super.close(); 552 } 553 } 554 } 555 556 /** 557 * A buffered output stream for a subprocess pipe file descriptor 558 * that allows the underlying file descriptor to be reclaimed when 559 * the process exits, via the processExited hook. 560 */ 561 private static class ProcessPipeOutputStream extends BufferedOutputStream { 562 ProcessPipeOutputStream(int fd) { 563 super(new FileOutputStream(newFileDescriptor(fd))); 564 } 565 566 /** Called by the process reaper thread when the process exits. */ 567 synchronized void processExited() { 568 OutputStream out = this.out; 569 if (out != null) { 570 try { 571 out.close(); 572 } catch (IOException ignored) { 573 // We know of no reason to get an IOException, but if 574 // we do, there's nothing else to do but carry on. 575 } 576 this.out = ProcessBuilder.NullOutputStream.INSTANCE; 577 } 578 } 579 } 580 581 // A FileInputStream that supports the deferment of the actual close 582 // operation until the last pending I/O operation on the stream has 583 // finished. This is required on Solaris because we must close the stdin 584 // and stdout streams in the destroy method in order to reclaim the 585 // underlying file descriptors. Doing so, however, causes any thread 586 // currently blocked in a read on one of those streams to receive an 587 // IOException("Bad file number"), which is incompatible with historical 588 // behavior. By deferring the close we allow any pending reads to see -1 589 // (EOF) as they did before. 590 // 591 private static class DeferredCloseInputStream extends FileInputStream 592 { 593 DeferredCloseInputStream(FileDescriptor fd) { 594 super(fd); 595 } 596 597 private Object lock = new Object(); // For the following fields 598 private boolean closePending = false; 599 private int useCount = 0; 600 private InputStream streamToClose; 601 602 private void raise() { 603 synchronized (lock) { 604 useCount++; 605 } 606 } 607 608 private void lower() throws IOException { 609 synchronized (lock) { 610 useCount--; 611 if (useCount == 0 && closePending) { 612 streamToClose.close(); 613 } 614 } 615 } 616 617 // stc is the actual stream to be closed; it might be this object, or 618 // it might be an upstream object for which this object is downstream. 619 // 620 private void closeDeferred(InputStream stc) throws IOException { 621 synchronized (lock) { 622 if (useCount == 0) { 623 stc.close(); 624 } else { 625 closePending = true; 626 streamToClose = stc; 627 } 628 } 629 } 630 631 public void close() throws IOException { 632 synchronized (lock) { 633 useCount = 0; 634 closePending = false; 635 } 636 super.close(); 637 } 638 639 public int read() throws IOException { 640 raise(); 641 try { 642 return super.read(); 643 } finally { 644 lower(); 645 } 646 } 647 648 public int read(byte[] b) throws IOException { 649 raise(); 650 try { 651 return super.read(b); 652 } finally { 653 lower(); 654 } 655 } 656 657 public int read(byte[] b, int off, int len) throws IOException { 658 raise(); 659 try { 660 return super.read(b, off, len); 661 } finally { 662 lower(); 663 } 664 } 665 666 public long skip(long n) throws IOException { 667 raise(); 668 try { 669 return super.skip(n); 670 } finally { 671 lower(); 672 } 673 } 674 675 public int available() throws IOException { 676 raise(); 677 try { 678 return super.available(); 679 } finally { 680 lower(); 681 } 682 } 683 } 684 685 /** 686 * A buffered input stream for a subprocess pipe file descriptor 687 * that allows the underlying file descriptor to be reclaimed when 688 * the process exits, via the processExited hook. 689 * 690 * This is tricky because we do not want the user-level InputStream to be 691 * closed until the user invokes close(), and we need to continue to be 692 * able to read any buffered data lingering in the OS pipe buffer. 693 * 694 * On AIX this is especially tricky, because the 'close()' system call 695 * will block if another thread is at the same time blocked in a file 696 * operation (e.g. 'read()') on the same file descriptor. We therefore 697 * combine 'ProcessPipeInputStream' approach used on Linux and Bsd 698 * with the DeferredCloseInputStream approach used on Solaris. This means 699 * that every potentially blocking operation on the file descriptor 700 * increments a counter before it is executed and decrements it once it 701 * finishes. The 'close()' operation will only be executed if there are 702 * no pending operations. Otherwise it is deferred after the last pending 703 * operation has finished. 704 * 705 */ 706 private static class DeferredCloseProcessPipeInputStream 707 extends BufferedInputStream { 708 709 private final Object closeLock = new Object(); 710 private int useCount = 0; 711 private boolean closePending = false; 712 713 DeferredCloseProcessPipeInputStream(int fd) { 714 super(new FileInputStream(newFileDescriptor(fd))); 715 } 716 717 private InputStream drainInputStream(InputStream in) 718 throws IOException { 719 int n = 0; 720 int j; 721 byte[] a = null; 722 synchronized (closeLock) { 723 if (buf == null) // asynchronous close()? 724 return null; // discard 725 j = in.available(); 726 } 727 while (j > 0) { 728 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 729 synchronized (closeLock) { 730 if (buf == null) // asynchronous close()? 731 return null; // discard 732 n += in.read(a, n, j); 733 j = in.available(); 734 } 735 } 736 return (a == null) ? 737 ProcessBuilder.NullInputStream.INSTANCE : 738 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); 739 } 740 741 /** Called by the process reaper thread when the process exits. */ 742 synchronized void processExited() { 743 try { 744 InputStream in = this.in; 745 if (in != null) { 746 InputStream stragglers = drainInputStream(in); 747 in.close(); 748 this.in = stragglers; 749 } 750 } catch (IOException ignored) { } 751 } 752 753 private void raise() { 754 synchronized (closeLock) { 755 useCount++; 756 } 757 } 758 759 private void lower() throws IOException { 760 synchronized (closeLock) { 761 useCount--; 762 if (useCount == 0 && closePending) { 763 closePending = false; 764 super.close(); 765 } 766 } 767 } 768 769 @Override 770 public int read() throws IOException { 771 raise(); 772 try { 773 return super.read(); 774 } finally { 775 lower(); 776 } 777 } 778 779 @Override 780 public int read(byte[] b) throws IOException { 781 raise(); 782 try { 783 return super.read(b); 784 } finally { 785 lower(); 786 } 787 } 788 789 @Override 790 public int read(byte[] b, int off, int len) throws IOException { 791 raise(); 792 try { 793 return super.read(b, off, len); 794 } finally { 795 lower(); 796 } 797 } 798 799 @Override 800 public long skip(long n) throws IOException { 801 raise(); 802 try { 803 return super.skip(n); 804 } finally { 805 lower(); 806 } 807 } 808 809 @Override 810 public int available() throws IOException { 811 raise(); 812 try { 813 return super.available(); 814 } finally { 815 lower(); 816 } 817 } 818 819 @Override 820 public void close() throws IOException { 821 // BufferedInputStream#close() is not synchronized unlike most other 822 // methods. Synchronizing helps avoid racing with drainInputStream(). 823 synchronized (closeLock) { 824 if (useCount == 0) { 825 super.close(); 826 } 827 else { 828 closePending = true; 829 } 830 } 831 } 832 } 833 }