Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/java/lang/UNIXProcess.java
32287 views
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
long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions
405
if (hasExited) return true;
406
if (timeout <= 0) return false;
407
408
long deadline = System.nanoTime() + remainingNanos;
409
do {
410
TimeUnit.NANOSECONDS.timedWait(this, remainingNanos);
411
if (hasExited) {
412
return true;
413
}
414
remainingNanos = deadline - System.nanoTime();
415
} while (remainingNanos > 0);
416
return hasExited;
417
}
418
419
public synchronized int exitValue() {
420
if (!hasExited) {
421
throw new IllegalThreadStateException("process hasn't exited");
422
}
423
return exitcode;
424
}
425
426
private static native void destroyProcess(int pid, boolean force);
427
428
private void destroy(boolean force) {
429
switch (platform) {
430
case LINUX:
431
case BSD:
432
case AIX:
433
// There is a risk that pid will be recycled, causing us to
434
// kill the wrong process! So we only terminate processes
435
// that appear to still be running. Even with this check,
436
// there is an unavoidable race condition here, but the window
437
// is very small, and OSes try hard to not recycle pids too
438
// soon, so this is quite safe.
439
synchronized (this) {
440
if (!hasExited)
441
destroyProcess(pid, force);
442
}
443
try { stdin.close(); } catch (IOException ignored) {}
444
try { stdout.close(); } catch (IOException ignored) {}
445
try { stderr.close(); } catch (IOException ignored) {}
446
break;
447
448
case SOLARIS:
449
// There is a risk that pid will be recycled, causing us to
450
// kill the wrong process! So we only terminate processes
451
// that appear to still be running. Even with this check,
452
// there is an unavoidable race condition here, but the window
453
// is very small, and OSes try hard to not recycle pids too
454
// soon, so this is quite safe.
455
synchronized (this) {
456
if (!hasExited)
457
destroyProcess(pid, force);
458
try {
459
stdin.close();
460
if (stdout_inner_stream != null)
461
stdout_inner_stream.closeDeferred(stdout);
462
if (stderr instanceof DeferredCloseInputStream)
463
((DeferredCloseInputStream) stderr)
464
.closeDeferred(stderr);
465
} catch (IOException e) {
466
// ignore
467
}
468
}
469
break;
470
471
default: throw new AssertionError("Unsupported platform: " + platform);
472
}
473
}
474
475
public void destroy() {
476
destroy(false);
477
}
478
479
@Override
480
public Process destroyForcibly() {
481
destroy(true);
482
return this;
483
}
484
485
@Override
486
public synchronized boolean isAlive() {
487
return !hasExited;
488
}
489
490
private static native void init();
491
492
static {
493
init();
494
}
495
496
/**
497
* A buffered input stream for a subprocess pipe file descriptor
498
* that allows the underlying file descriptor to be reclaimed when
499
* the process exits, via the processExited hook.
500
*
501
* This is tricky because we do not want the user-level InputStream to be
502
* closed until the user invokes close(), and we need to continue to be
503
* able to read any buffered data lingering in the OS pipe buffer.
504
*/
505
private static class ProcessPipeInputStream extends BufferedInputStream {
506
private final Object closeLock = new Object();
507
508
ProcessPipeInputStream(int fd) {
509
super(new FileInputStream(newFileDescriptor(fd)));
510
}
511
private static byte[] drainInputStream(InputStream in)
512
throws IOException {
513
int n = 0;
514
int j;
515
byte[] a = null;
516
while ((j = in.available()) > 0) {
517
a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
518
n += in.read(a, n, j);
519
}
520
return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
521
}
522
523
/** Called by the process reaper thread when the process exits. */
524
synchronized void processExited() {
525
synchronized (closeLock) {
526
try {
527
InputStream in = this.in;
528
// this stream is closed if and only if: in == null
529
if (in != null) {
530
byte[] stragglers = drainInputStream(in);
531
in.close();
532
this.in = (stragglers == null) ?
533
ProcessBuilder.NullInputStream.INSTANCE :
534
new ByteArrayInputStream(stragglers);
535
}
536
} catch (IOException ignored) {}
537
}
538
}
539
540
@Override
541
public void close() throws IOException {
542
// BufferedInputStream#close() is not synchronized unlike most other
543
// methods. Synchronizing helps avoid race with processExited().
544
synchronized (closeLock) {
545
super.close();
546
}
547
}
548
}
549
550
/**
551
* A buffered output stream for a subprocess pipe file descriptor
552
* that allows the underlying file descriptor to be reclaimed when
553
* the process exits, via the processExited hook.
554
*/
555
private static class ProcessPipeOutputStream extends BufferedOutputStream {
556
ProcessPipeOutputStream(int fd) {
557
super(new FileOutputStream(newFileDescriptor(fd)));
558
}
559
560
/** Called by the process reaper thread when the process exits. */
561
synchronized void processExited() {
562
OutputStream out = this.out;
563
if (out != null) {
564
try {
565
out.close();
566
} catch (IOException ignored) {
567
// We know of no reason to get an IOException, but if
568
// we do, there's nothing else to do but carry on.
569
}
570
this.out = ProcessBuilder.NullOutputStream.INSTANCE;
571
}
572
}
573
}
574
575
// A FileInputStream that supports the deferment of the actual close
576
// operation until the last pending I/O operation on the stream has
577
// finished. This is required on Solaris because we must close the stdin
578
// and stdout streams in the destroy method in order to reclaim the
579
// underlying file descriptors. Doing so, however, causes any thread
580
// currently blocked in a read on one of those streams to receive an
581
// IOException("Bad file number"), which is incompatible with historical
582
// behavior. By deferring the close we allow any pending reads to see -1
583
// (EOF) as they did before.
584
//
585
private static class DeferredCloseInputStream extends FileInputStream
586
{
587
DeferredCloseInputStream(FileDescriptor fd) {
588
super(fd);
589
}
590
591
private Object lock = new Object(); // For the following fields
592
private boolean closePending = false;
593
private int useCount = 0;
594
private InputStream streamToClose;
595
596
private void raise() {
597
synchronized (lock) {
598
useCount++;
599
}
600
}
601
602
private void lower() throws IOException {
603
synchronized (lock) {
604
useCount--;
605
if (useCount == 0 && closePending) {
606
streamToClose.close();
607
}
608
}
609
}
610
611
// stc is the actual stream to be closed; it might be this object, or
612
// it might be an upstream object for which this object is downstream.
613
//
614
private void closeDeferred(InputStream stc) throws IOException {
615
synchronized (lock) {
616
if (useCount == 0) {
617
stc.close();
618
} else {
619
closePending = true;
620
streamToClose = stc;
621
}
622
}
623
}
624
625
public void close() throws IOException {
626
synchronized (lock) {
627
useCount = 0;
628
closePending = false;
629
}
630
super.close();
631
}
632
633
public int read() throws IOException {
634
raise();
635
try {
636
return super.read();
637
} finally {
638
lower();
639
}
640
}
641
642
public int read(byte[] b) throws IOException {
643
raise();
644
try {
645
return super.read(b);
646
} finally {
647
lower();
648
}
649
}
650
651
public int read(byte[] b, int off, int len) throws IOException {
652
raise();
653
try {
654
return super.read(b, off, len);
655
} finally {
656
lower();
657
}
658
}
659
660
public long skip(long n) throws IOException {
661
raise();
662
try {
663
return super.skip(n);
664
} finally {
665
lower();
666
}
667
}
668
669
public int available() throws IOException {
670
raise();
671
try {
672
return super.available();
673
} finally {
674
lower();
675
}
676
}
677
}
678
679
/**
680
* A buffered input stream for a subprocess pipe file descriptor
681
* that allows the underlying file descriptor to be reclaimed when
682
* the process exits, via the processExited hook.
683
*
684
* This is tricky because we do not want the user-level InputStream to be
685
* closed until the user invokes close(), and we need to continue to be
686
* able to read any buffered data lingering in the OS pipe buffer.
687
*
688
* On AIX this is especially tricky, because the 'close()' system call
689
* will block if another thread is at the same time blocked in a file
690
* operation (e.g. 'read()') on the same file descriptor. We therefore
691
* combine 'ProcessPipeInputStream' approach used on Linux and Bsd
692
* with the DeferredCloseInputStream approach used on Solaris. This means
693
* that every potentially blocking operation on the file descriptor
694
* increments a counter before it is executed and decrements it once it
695
* finishes. The 'close()' operation will only be executed if there are
696
* no pending operations. Otherwise it is deferred after the last pending
697
* operation has finished.
698
*
699
*/
700
private static class DeferredCloseProcessPipeInputStream
701
extends BufferedInputStream {
702
703
private final Object closeLock = new Object();
704
private int useCount = 0;
705
private boolean closePending = false;
706
707
DeferredCloseProcessPipeInputStream(int fd) {
708
super(new FileInputStream(newFileDescriptor(fd)));
709
}
710
711
private InputStream drainInputStream(InputStream in)
712
throws IOException {
713
int n = 0;
714
int j;
715
byte[] a = null;
716
synchronized (closeLock) {
717
if (buf == null) // asynchronous close()?
718
return null; // discard
719
j = in.available();
720
}
721
while (j > 0) {
722
a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
723
synchronized (closeLock) {
724
if (buf == null) // asynchronous close()?
725
return null; // discard
726
n += in.read(a, n, j);
727
j = in.available();
728
}
729
}
730
return (a == null) ?
731
ProcessBuilder.NullInputStream.INSTANCE :
732
new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
733
}
734
735
/** Called by the process reaper thread when the process exits. */
736
synchronized void processExited() {
737
try {
738
InputStream in = this.in;
739
if (in != null) {
740
InputStream stragglers = drainInputStream(in);
741
in.close();
742
this.in = stragglers;
743
}
744
} catch (IOException ignored) { }
745
}
746
747
private void raise() {
748
synchronized (closeLock) {
749
useCount++;
750
}
751
}
752
753
private void lower() throws IOException {
754
synchronized (closeLock) {
755
useCount--;
756
if (useCount == 0 && closePending) {
757
closePending = false;
758
super.close();
759
}
760
}
761
}
762
763
@Override
764
public int read() throws IOException {
765
raise();
766
try {
767
return super.read();
768
} finally {
769
lower();
770
}
771
}
772
773
@Override
774
public int read(byte[] b) throws IOException {
775
raise();
776
try {
777
return super.read(b);
778
} finally {
779
lower();
780
}
781
}
782
783
@Override
784
public int read(byte[] b, int off, int len) throws IOException {
785
raise();
786
try {
787
return super.read(b, off, len);
788
} finally {
789
lower();
790
}
791
}
792
793
@Override
794
public long skip(long n) throws IOException {
795
raise();
796
try {
797
return super.skip(n);
798
} finally {
799
lower();
800
}
801
}
802
803
@Override
804
public int available() throws IOException {
805
raise();
806
try {
807
return super.available();
808
} finally {
809
lower();
810
}
811
}
812
813
@Override
814
public void close() throws IOException {
815
// BufferedInputStream#close() is not synchronized unlike most other
816
// methods. Synchronizing helps avoid racing with drainInputStream().
817
synchronized (closeLock) {
818
if (useCount == 0) {
819
super.close();
820
}
821
else {
822
closePending = true;
823
}
824
}
825
}
826
}
827
}
828
829