335 * This is tricky because we do not want the user-level InputStream to be |
335 * This is tricky because we do not want the user-level InputStream to be |
336 * closed until the user invokes close(), and we need to continue to be |
336 * closed until the user invokes close(), and we need to continue to be |
337 * able to read any buffered data lingering in the OS pipe buffer. |
337 * able to read any buffered data lingering in the OS pipe buffer. |
338 */ |
338 */ |
339 static class ProcessPipeInputStream extends BufferedInputStream { |
339 static class ProcessPipeInputStream extends BufferedInputStream { |
|
340 private final Object closeLock = new Object(); |
|
341 |
340 ProcessPipeInputStream(int fd) { |
342 ProcessPipeInputStream(int fd) { |
341 super(new FileInputStream(newFileDescriptor(fd))); |
343 super(new FileInputStream(newFileDescriptor(fd))); |
342 } |
344 } |
343 |
345 |
344 private static byte[] drainInputStream(InputStream in) |
346 private InputStream drainInputStream(InputStream in) |
345 throws IOException { |
347 throws IOException { |
346 if (in == null) return null; |
|
347 int n = 0; |
348 int n = 0; |
348 int j; |
349 int j; |
349 byte[] a = null; |
350 byte[] a = null; |
350 while ((j = in.available()) > 0) { |
351 synchronized (closeLock) { |
|
352 if (buf == null) // asynchronous close()? |
|
353 return null; // discard |
|
354 j = in.available(); |
|
355 } |
|
356 while (j > 0) { |
351 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); |
357 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); |
352 n += in.read(a, n, j); |
358 synchronized (closeLock) { |
|
359 if (buf == null) // asynchronous close()? |
|
360 return null; // discard |
|
361 n += in.read(a, n, j); |
|
362 j = in.available(); |
|
363 } |
353 } |
364 } |
354 return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); |
365 return (a == null) ? |
|
366 ProcessBuilder.NullInputStream.INSTANCE : |
|
367 new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); |
355 } |
368 } |
356 |
369 |
357 /** Called by the process reaper thread when the process exits. */ |
370 /** Called by the process reaper thread when the process exits. */ |
358 synchronized void processExited() { |
371 synchronized void processExited() { |
359 // Most BufferedInputStream methods are synchronized, but close() |
|
360 // is not, and so we have to handle concurrent racing close(). |
|
361 try { |
372 try { |
362 InputStream in = this.in; |
373 InputStream in = this.in; |
363 if (in != null) { |
374 if (in != null) { |
364 byte[] stragglers = drainInputStream(in); |
375 InputStream stragglers = drainInputStream(in); |
365 in.close(); |
376 in.close(); |
366 this.in = (stragglers == null) ? |
377 this.in = stragglers; |
367 ProcessBuilder.NullInputStream.INSTANCE : |
|
368 new ByteArrayInputStream(stragglers); |
|
369 if (buf == null) // asynchronous close()? |
|
370 this.in = null; |
|
371 } |
378 } |
372 } catch (IOException ignored) { |
379 } catch (IOException ignored) { } |
373 // probably an asynchronous close(). |
380 } |
|
381 |
|
382 @Override |
|
383 public void close() throws IOException { |
|
384 // BufferedInputStream#close() is not synchronized unlike most other methods. |
|
385 // Synchronizing helps avoid racing with drainInputStream(). |
|
386 synchronized (closeLock) { |
|
387 super.close(); |
374 } |
388 } |
375 } |
389 } |
376 } |
390 } |
377 |
391 |
378 /** |
392 /** |