StreamCopyThread.java

  1. /*
  2.  * Copyright (C) 2009-2010, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.util.io;

  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.io.InterruptedIOException;
  14. import java.io.OutputStream;

  15. /**
  16.  * Thread to copy from an input stream to an output stream.
  17.  */
  18. public class StreamCopyThread extends Thread {
  19.     private static final int BUFFER_SIZE = 1024;

  20.     private final InputStream src;

  21.     private final OutputStream dst;

  22.     private volatile boolean done;

  23.     /** Lock held by flush to avoid interrupting a write. */
  24.     private final Object writeLock;

  25.     /**
  26.      * Create a thread to copy data from an input stream to an output stream.
  27.      *
  28.      * @param i
  29.      *            stream to copy from. The thread terminates when this stream
  30.      *            reaches EOF. The thread closes this stream before it exits.
  31.      * @param o
  32.      *            stream to copy into. The destination stream is automatically
  33.      *            closed when the thread terminates.
  34.      */
  35.     public StreamCopyThread(InputStream i, OutputStream o) {
  36.         setName(Thread.currentThread().getName() + "-StreamCopy"); //$NON-NLS-1$
  37.         src = i;
  38.         dst = o;
  39.         writeLock = new Object();
  40.     }

  41.     /**
  42.      * Request that the thread terminate, and wait for it.
  43.      * <p>
  44.      * This method signals to the copy thread that it should stop as soon as
  45.      * there is no more IO occurring.
  46.      *
  47.      * @throws java.lang.InterruptedException
  48.      *             the calling thread was interrupted.
  49.      */
  50.     public void halt() throws InterruptedException {
  51.         for (;;) {
  52.             join(250 /* milliseconds */);
  53.             if (isAlive()) {
  54.                 done = true;
  55.                 interrupt();
  56.             } else
  57.                 break;
  58.         }
  59.     }

  60.     /** {@inheritDoc} */
  61.     @Override
  62.     public void run() {
  63.         try {
  64.             final byte[] buf = new byte[BUFFER_SIZE];
  65.             boolean readInterrupted = false;
  66.             for (;;) {
  67.                 try {
  68.                     if (readInterrupted) {
  69.                         synchronized (writeLock) {
  70.                             boolean interruptedAgain = Thread.interrupted();
  71.                             dst.flush();
  72.                             if (interruptedAgain) {
  73.                                 interrupt();
  74.                             }
  75.                         }
  76.                         readInterrupted = false;
  77.                     }

  78.                     if (done)
  79.                         break;

  80.                     final int n;
  81.                     try {
  82.                         n = src.read(buf);
  83.                     } catch (InterruptedIOException wakey) {
  84.                         readInterrupted = true;
  85.                         continue;
  86.                     }
  87.                     if (n < 0)
  88.                         break;

  89.                     synchronized (writeLock) {
  90.                         boolean writeInterrupted = Thread.interrupted();
  91.                         dst.write(buf, 0, n);
  92.                         if (writeInterrupted) {
  93.                             interrupt();
  94.                         }
  95.                     }
  96.                 } catch (IOException e) {
  97.                     break;
  98.                 }
  99.             }
  100.         } finally {
  101.             try {
  102.                 src.close();
  103.             } catch (IOException e) {
  104.                 // Ignore IO errors on close
  105.             }
  106.             try {
  107.                 dst.close();
  108.             } catch (IOException e) {
  109.                 // Ignore IO errors on close
  110.             }
  111.         }
  112.     }
  113. }