View Javadoc
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  
11  package org.eclipse.jgit.util.io;
12  
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.InterruptedIOException;
16  import java.io.OutputStream;
17  
18  /**
19   * Thread to copy from an input stream to an output stream.
20   */
21  public class StreamCopyThread extends Thread {
22  	private static final int BUFFER_SIZE = 1024;
23  
24  	private final InputStream src;
25  
26  	private final OutputStream dst;
27  
28  	private volatile boolean done;
29  
30  	/** Lock held by flush to avoid interrupting a write. */
31  	private final Object writeLock;
32  
33  	/**
34  	 * Create a thread to copy data from an input stream to an output stream.
35  	 *
36  	 * @param i
37  	 *            stream to copy from. The thread terminates when this stream
38  	 *            reaches EOF. The thread closes this stream before it exits.
39  	 * @param o
40  	 *            stream to copy into. The destination stream is automatically
41  	 *            closed when the thread terminates.
42  	 */
43  	public StreamCopyThread(InputStream i, OutputStream o) {
44  		setName(Thread.currentThread().getName() + "-StreamCopy"); //$NON-NLS-1$
45  		src = i;
46  		dst = o;
47  		writeLock = new Object();
48  	}
49  
50  	/**
51  	 * Request that the thread terminate, and wait for it.
52  	 * <p>
53  	 * This method signals to the copy thread that it should stop as soon as
54  	 * there is no more IO occurring.
55  	 *
56  	 * @throws java.lang.InterruptedException
57  	 *             the calling thread was interrupted.
58  	 */
59  	public void halt() throws InterruptedException {
60  		for (;;) {
61  			join(250 /* milliseconds */);
62  			if (isAlive()) {
63  				done = true;
64  				interrupt();
65  			} else
66  				break;
67  		}
68  	}
69  
70  	/** {@inheritDoc} */
71  	@Override
72  	public void run() {
73  		try {
74  			final byte[] buf = new byte[BUFFER_SIZE];
75  			boolean readInterrupted = false;
76  			for (;;) {
77  				try {
78  					if (readInterrupted) {
79  						synchronized (writeLock) {
80  							boolean interruptedAgain = Thread.interrupted();
81  							dst.flush();
82  							if (interruptedAgain) {
83  								interrupt();
84  							}
85  						}
86  						readInterrupted = false;
87  					}
88  
89  					if (done)
90  						break;
91  
92  					final int n;
93  					try {
94  						n = src.read(buf);
95  					} catch (InterruptedIOException wakey) {
96  						readInterrupted = true;
97  						continue;
98  					}
99  					if (n < 0)
100 						break;
101 
102 					synchronized (writeLock) {
103 						boolean writeInterrupted = Thread.interrupted();
104 						dst.write(buf, 0, n);
105 						if (writeInterrupted) {
106 							interrupt();
107 						}
108 					}
109 				} catch (IOException e) {
110 					break;
111 				}
112 			}
113 		} finally {
114 			try {
115 				src.close();
116 			} catch (IOException e) {
117 				// Ignore IO errors on close
118 			}
119 			try {
120 				dst.close();
121 			} catch (IOException e) {
122 				// Ignore IO errors on close
123 			}
124 		}
125 	}
126 }