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