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