source: Common/AsyncStreamReaderFast.cs

Last change on this file was 15, checked in by chronos, 6 months ago
  • Modified: Updated files.
File size: 5.9 KB
Line 
1using System;
2using System.IO;
3using System.Text;
4using System.Threading;
5using System.Diagnostics;
6
7namespace Common
8{
9 internal delegate void UserCallBack(string data);
10
11 internal class AsyncStreamReaderFast : IDisposable
12 {
13 internal const int DefaultBufferSize = 16000; // Byte buffer size
14 private const int MinBufferSize = 128;
15
16 private Stream stream;
17 private Encoding encoding;
18 private Decoder decoder;
19 private byte[] byteBuffer;
20 private char[] charBuffer;
21 // Record the number of valid bytes in the byteBuffer, for a few checks.
22
23 // This is the maximum number of chars we can get from one call to
24 // ReadBuffer. Used so ReadBuffer can tell when to copy data into
25 // a user's char[] directly, instead of our internal char[].
26 private int maxCharsPerBuffer;
27
28 // Store a backpointer to the process class, to check for user callbacks
29 private StringBuilder sb;
30
31 // Delegate to call user function.
32 private UserCallBack userCallBack;
33
34 // Internal Cancel operation
35 private bool cancelOperation;
36 private ManualResetEvent eofEvent;
37
38 internal AsyncStreamReaderFast(Process process, Stream stream, UserCallBack callback, Encoding encoding)
39 : this(process, stream, callback, encoding, DefaultBufferSize)
40 {
41 }
42
43 // Creates a new AsyncStreamReader for the given stream. The
44 // character encoding is set by encoding and the buffer size,
45 // in number of 16-bit characters, is set by bufferSize.
46 //
47 internal AsyncStreamReaderFast(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
48 {
49 Debug.Assert(process != null && stream != null && encoding != null && callback != null, "Invalid arguments!");
50 Debug.Assert(stream.CanRead, "Stream must be readable!");
51 Debug.Assert(bufferSize > 0, "Invalid buffer size!");
52
53 Init(process, stream, callback, encoding, bufferSize);
54 }
55
56 private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int bufferSize)
57 {
58 this.stream = stream;
59 this.encoding = encoding;
60 this.userCallBack = callback;
61 decoder = encoding.GetDecoder();
62 if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
63 byteBuffer = new byte[bufferSize];
64 maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
65 charBuffer = new char[maxCharsPerBuffer];
66 cancelOperation = false;
67 eofEvent = new ManualResetEvent(false);
68 sb = new StringBuilder(charBuffer.Length);
69 }
70
71 public virtual void Close()
72 {
73 Dispose(true);
74 }
75
76 void IDisposable.Dispose()
77 {
78 Dispose(true);
79 }
80
81 protected virtual void Dispose(bool disposing)
82 {
83 if (disposing)
84 {
85 if (stream != null)
86 stream.Close();
87 }
88 if (stream != null)
89 {
90 stream = null;
91 encoding = null;
92 decoder = null;
93 byteBuffer = null;
94 charBuffer = null;
95 }
96
97 if (eofEvent != null)
98 {
99 eofEvent.Close();
100 eofEvent = null;
101 }
102 }
103
104 public virtual Encoding CurrentEncoding => encoding;
105
106 public virtual Stream BaseStream => stream;
107
108 // User calls BeginRead to start the asynchronous read
109 public void BeginRead()
110 {
111 if (cancelOperation)
112 {
113 cancelOperation = false;
114 }
115
116 stream.BeginRead(byteBuffer, 0, byteBuffer.Length, ReadBuffer, null);
117 }
118
119 internal void CancelOperation()
120 {
121 cancelOperation = true;
122 }
123
124 // This is the async callback function. Only one thread could/should call this.
125 private void ReadBuffer(IAsyncResult ar)
126 {
127 int byteLen;
128
129 try
130 {
131 byteLen = stream.EndRead(ar);
132 }
133 catch (IOException)
134 {
135 // We should ideally consume errors from operations getting cancelled
136 // so that we don't crash the unsuspecting parent with an unhandled exc.
137 // This seems to come in 2 forms of exceptions (depending on platform and scenario),
138 // namely OperationCanceledException and IOException (for errorcode that we don't
139 // map explicitly).
140 byteLen = 0; // Treat this as EOF
141 }
142 catch (OperationCanceledException)
143 {
144 // We should consume any OperationCanceledException from child read here
145 // so that we don't crash the parent with an unhandled exc
146 byteLen = 0; // Treat this as EOF
147 }
148
149 if (byteLen == 0)
150 {
151 eofEvent.Set();
152 }
153 else
154 {
155 int charLen = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, 0);
156 sb.Length = 0;
157 sb.Append(charBuffer, 0, charLen);
158 userCallBack(sb.ToString());
159 stream.BeginRead(byteBuffer, 0, byteBuffer.Length, ReadBuffer, null);
160 }
161 }
162
163 // Wait until we hit EOF. This is called from Process.WaitForExit
164 // We will lose some information if we don't do this.
165 internal void WaitUtilEOF()
166 {
167 if (eofEvent == null) return;
168 eofEvent.WaitOne();
169 eofEvent.Close();
170 eofEvent = null;
171 }
172 }
173}
Note: See TracBrowser for help on using the repository browser.