1 /* 2 * Copyright (c) 1995, 2019, 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 TimeUnit.NANOSECONDS.timedWait(this, remainingNanos); 412 if (hasExited) { 413 return true; 414 } 415 remainingNanos = deadline - System.nanoTime(); 416 } while (remainingNanos > 0); 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 synchronized boolean isAlive() { 488 return !hasExited; 489 } 490 491 private static native void init(); 492 493 static { 494 init(); 495 } 496 497 /** 498 * A buffered input stream for a subprocess pipe file descriptor 499 * that allows the underlying file descriptor to be reclaimed when 500 * the process exits, via the processExited hook. 501 * 502 * This is tricky because we do not want the user-level InputStream to be 503 * closed until the user invokes close(), and we need to continue to be 504 * able to read any buffered data lingering in the OS pipe buffer. 505 */ 506 private static class ProcessPipeInputStream extends BufferedInputStream { 507 private final Object closeLock = new Object(); 508 509 ProcessPipeInputStream(int fd) { 510 super(new FileInputStream(newFileDescriptor(fd))); 511 } 512 private static byte[] drainInputStream(InputStream in) 513 throws IOException { 514 int n = 0; 515 int j; 516 byte[] a = null; 517 while ((j = in.available()) > 0) { 518 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 519 n += in.read(a, n, j); 520 } 521 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); 522 } 523 524 /** Called by the process reaper thread when the process exits. */ 525 synchronized void processExited() { 526 synchronized (closeLock) { 527 try { 528 InputStream in = this.in; 529 // this stream is closed if and only if: in == null 530 if (in != null) { 531 byte[] stragglers = drainInputStream(in); 532 in.close(); 533 this.in = (stragglers == null) ? 534 ProcessBuilder.NullInputStream.INSTANCE : 535 new ByteArrayInputStream(stragglers); 536 } 537 } catch (IOException ignored) {} 538 } 539 } 540 541 @Override 542 public void close() throws IOException { 543 // BufferedInputStream#close() is not synchronized unlike most other 544 // methods. Synchronizing helps avoid race with processExited(). 545 synchronized (closeLock) { 546 super.close(); 547 } 548 } 549 } 550 551 /** 552 * A buffered output stream for a subprocess pipe file descriptor 553 * that allows the underlying file descriptor to be reclaimed when 554 * the process exits, via the processExited hook. 555 */ 556 private static class ProcessPipeOutputStream extends BufferedOutputStream { 557 ProcessPipeOutputStream(int fd) { 558 super(new FileOutputStream(newFileDescriptor(fd))); 559 } 560 561 /** Called by the process reaper thread when the process exits. */ 562 synchronized void processExited() { 563 OutputStream out = this.out; 564 if (out != null) { 565 try { 566 out.close(); 567 } catch (IOException ignored) { 568 // We know of no reason to get an IOException, but if 569 // we do, there's nothing else to do but carry on. 570 } 571 this.out = ProcessBuilder.NullOutputStream.INSTANCE; 572 } 573 } 574 } 575 576 // A FileInputStream that supports the deferment of the actual close 577 // operation until the last pending I/O operation on the stream has 578 // finished. This is required on Solaris because we must close the stdin 579 // and stdout streams in the destroy method in order to reclaim the 580 // underlying file descriptors. Doing so, however, causes any thread 581 // currently blocked in a read on one of those streams to receive an 582 // IOException("Bad file number"), which is incompatible with historical 583 // behavior. By deferring the close we allow any pending reads to see -1 584 // (EOF) as they did before. 585 // 586 private static class DeferredCloseInputStream extends FileInputStream 587 { 588 DeferredCloseInputStream(FileDescriptor fd) { 589 super(fd); 590 } 591 592 private Object lock = new Object(); // For the following fields 593 private boolean closePending = false; 594 private int useCount = 0; 595 private InputStream streamToClose; 596 597 private void raise() { 598 synchronized (lock) { 599 useCount++; 600 } 601 } 602 603 private void lower() throws IOException { 604 synchronized (lock) { 605 useCount--; 606 if (useCount == 0 && closePending) { 607 streamToClose.close(); 608 } 609 } 610 } 611 612 // stc is the actual stream to be closed; it might be this object, or 613 // it might be an upstream object for which this object is downstream. 614 // 615 private void closeDeferred(InputStream stc) throws IOException { 616 synchronized (lock) { 617 if (useCount == 0) { 618 stc.close(); 619 } else { 620 closePending = true; 621 streamToClose = stc; 622 } 623 } 624 } 625 626 public void close() throws IOException { 627 synchronized (lock) { 628 useCount = 0; 629 closePending = false; 630 } 631 super.close(); 632 } 633 634 public int read() throws IOException { 635 raise(); 636 try { 637 return super.read(); 638 } finally { 639 lower(); 640 } 641 } 642 643 public int read(byte[] b) throws IOException { 644 raise(); 645 try { 646 return super.read(b); 647 } finally { 648 lower(); 649 } 650 } 651 652 public int read(byte[] b, int off, int len) throws IOException { 653 raise(); 654 try { 655 return super.read(b, off, len); 656 } finally { 657 lower(); 658 } 659 } 660 661 public long skip(long n) throws IOException { 662 raise(); 663 try { 664 return super.skip(n); 665 } finally { 666 lower(); 667 } 668 } 669 670 public int available() throws IOException { 671 raise(); 672 try { 673 return super.available(); 674 } finally { 675 lower(); 676 } 677 } 678 } 679 680 /** 681 * A buffered input stream for a subprocess pipe file descriptor 682 * that allows the underlying file descriptor to be reclaimed when 683 * the process exits, via the processExited hook. 684 * 685 * This is tricky because we do not want the user-level InputStream to be 686 * closed until the user invokes close(), and we need to continue to be 687 * able to read any buffered data lingering in the OS pipe buffer. 688 * 689 * On AIX this is especially tricky, because the 'close()' system call 690 * will block if another thread is at the same time blocked in a file 691 * operation (e.g. 'read()') on the same file descriptor. We therefore 692 * combine 'ProcessPipeInputStream' approach used on Linux and Bsd 693 * with the DeferredCloseInputStream approach used on Solaris. This means 694 * that every potentially blocking operation on the file descriptor 695 * increments a counter before it is executed and decrements it once it 696 * finishes. The 'close()' operation will only be executed if there are 697 * no pending operations. Otherwise it is deferred after the last pending 698 * operation has finished. 699 * 700 */ 701 private static class DeferredCloseProcessPipeInputStream 702 extends BufferedInputStream { 703 704 private final Object closeLock = new Object(); 705 private int useCount = 0; 706 private boolean closePending = false; 707 708 DeferredCloseProcessPipeInputStream(int fd) { 709 super(new FileInputStream(newFileDescriptor(fd))); 710 } 711 712 private InputStream drainInputStream(InputStream in) 713 throws IOException { 714 int n = 0; 715 int j; 716 byte[] a = null; 717 synchronized (closeLock) { 718 if (buf == null) // asynchronous close()? 719 return null; // discard 720 j = in.available(); 721 } 722 while (j > 0) { 723 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); 724 synchronized (closeLock) { 725 if (buf == null) // asynchronous close()? 726 return null; // discard 727 n += in.read(a, n, j); 728 j = in.available(); 729 } 730 } 731 return (a == null) ? 732 ProcessBuilder.NullInputStream.INSTANCE : 733 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); 734 } 735 736 /** Called by the process reaper thread when the process exits. */ 737 synchronized void processExited() { 738 try { 739 InputStream in = this.in; 740 if (in != null) { 741 InputStream stragglers = drainInputStream(in); 742 in.close(); 743 this.in = stragglers; 744 } 745 } catch (IOException ignored) { } 746 } 747 748 private void raise() { 749 synchronized (closeLock) { 750 useCount++; 751 } 752 } 753 754 private void lower() throws IOException { 755 synchronized (closeLock) { 756 useCount--; 757 if (useCount == 0 && closePending) { 758 closePending = false; 759 super.close(); 760 } 761 } 762 } 763 764 @Override 765 public int read() throws IOException { 766 raise(); 767 try { 768 return super.read(); 769 } finally { 770 lower(); 771 } 772 } 773 774 @Override 775 public int read(byte[] b) throws IOException { 776 raise(); 777 try { 778 return super.read(b); 779 } finally { 780 lower(); 781 } 782 } 783 784 @Override 785 public int read(byte[] b, int off, int len) throws IOException { 786 raise(); 787 try { 788 return super.read(b, off, len); 789 } finally { 790 lower(); 791 } 792 } 793 794 @Override 795 public long skip(long n) throws IOException { 796 raise(); 797 try { 798 return super.skip(n); 799 } finally { 800 lower(); 801 } 802 } 803 804 @Override 805 public int available() throws IOException { 806 raise(); 807 try { 808 return super.available(); 809 } finally { 810 lower(); 811 } 812 } 813 814 @Override 815 public void close() throws IOException { 816 // BufferedInputStream#close() is not synchronized unlike most other 817 // methods. Synchronizing helps avoid racing with drainInputStream(). 818 synchronized (closeLock) { 819 if (useCount == 0) { 820 super.close(); 821 } 822 else { 823 closePending = true; 824 } 825 } 826 } 827 } 828 }