You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
7.4 KiB

4 years ago
  1. #if MYSQL_6_9
  2. // Copyright (c) 2009-2010 Sun Microsystems, Inc.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.IO;
  25. using System.Diagnostics;
  26. using Externals.MySql.Data.Common;
  27. namespace Externals.MySql.Data.MySqlClient
  28. {
  29. /// <summary>
  30. /// Stream that supports timeout of IO operations.
  31. /// This class is used is used to support timeouts for SQL command, where a
  32. /// typical operation involves several network reads/writes.
  33. /// Timeout here is defined as the accumulated duration of all IO operations.
  34. /// </summary>
  35. internal class TimedStream : Stream
  36. {
  37. Stream baseStream;
  38. int timeout;
  39. int lastReadTimeout;
  40. int lastWriteTimeout;
  41. LowResolutionStopwatch stopwatch;
  42. bool isClosed;
  43. internal bool IsClosed { get { return isClosed; } }
  44. enum IOKind
  45. {
  46. Read,
  47. Write
  48. };
  49. /// <summary>
  50. /// Construct a TimedStream
  51. /// </summary>
  52. /// <param name="baseStream"> Undelying stream</param>
  53. public TimedStream(Stream baseStream)
  54. {
  55. this.baseStream = baseStream;
  56. timeout = baseStream.ReadTimeout;
  57. isClosed = false;
  58. stopwatch = new LowResolutionStopwatch();
  59. }
  60. /// <summary>
  61. /// Figure out whether it is necessary to reset timeout on stream.
  62. /// We track the current value of timeout and try to avoid
  63. /// changing it too often, because setting Read/WriteTimeout property
  64. /// on network stream maybe a slow operation that involves a system call
  65. /// (setsockopt). Therefore, we allow a small difference, and do not
  66. /// reset timeout if current value is slightly greater than the requested
  67. /// one (within 0.1 second).
  68. /// </summary>
  69. private bool ShouldResetStreamTimeout(int currentValue, int newValue)
  70. {
  71. if (newValue == System.Threading.Timeout.Infinite
  72. && currentValue != newValue)
  73. return true;
  74. if (newValue > currentValue)
  75. return true;
  76. if (currentValue >= newValue + 100)
  77. return true;
  78. return false;
  79. }
  80. private void StartTimer(IOKind op)
  81. {
  82. int streamTimeout;
  83. if (timeout == System.Threading.Timeout.Infinite)
  84. streamTimeout = System.Threading.Timeout.Infinite;
  85. else
  86. streamTimeout = timeout - (int)stopwatch.ElapsedMilliseconds;
  87. if (op == IOKind.Read)
  88. {
  89. if (ShouldResetStreamTimeout(lastReadTimeout, streamTimeout))
  90. {
  91. baseStream.ReadTimeout = streamTimeout;
  92. lastReadTimeout = streamTimeout;
  93. }
  94. }
  95. else
  96. {
  97. if (ShouldResetStreamTimeout(lastWriteTimeout, streamTimeout))
  98. {
  99. baseStream.WriteTimeout = streamTimeout;
  100. lastWriteTimeout = streamTimeout;
  101. }
  102. }
  103. if (timeout == System.Threading.Timeout.Infinite)
  104. return;
  105. stopwatch.Start();
  106. }
  107. private void StopTimer()
  108. {
  109. if (timeout == System.Threading.Timeout.Infinite)
  110. return;
  111. stopwatch.Stop();
  112. // Normally, a timeout exception would be thrown by stream itself,
  113. // since we set the read/write timeout for the stream. However
  114. // there is a gap between end of IO operation and stopping the
  115. // stop watch, and it makes it possible for timeout to exceed
  116. // even after IO completed successfully.
  117. if (stopwatch.ElapsedMilliseconds > timeout)
  118. {
  119. ResetTimeout(System.Threading.Timeout.Infinite);
  120. throw new TimeoutException("Timeout in IO operation");
  121. }
  122. }
  123. public override bool CanRead
  124. {
  125. get { return baseStream.CanRead; }
  126. }
  127. public override bool CanSeek
  128. {
  129. get { return baseStream.CanSeek; }
  130. }
  131. public override bool CanWrite
  132. {
  133. get { return baseStream.CanWrite; }
  134. }
  135. public override void Flush()
  136. {
  137. try
  138. {
  139. StartTimer(IOKind.Write);
  140. baseStream.Flush();
  141. StopTimer();
  142. }
  143. catch (Exception e)
  144. {
  145. HandleException(e);
  146. throw;
  147. }
  148. }
  149. public override long Length
  150. {
  151. get { return baseStream.Length; }
  152. }
  153. public override long Position
  154. {
  155. get
  156. {
  157. return baseStream.Position;
  158. }
  159. set
  160. {
  161. baseStream.Position = value;
  162. }
  163. }
  164. public override int Read(byte[] buffer, int offset, int count)
  165. {
  166. try
  167. {
  168. StartTimer(IOKind.Read);
  169. int retval = baseStream.Read(buffer, offset, count);
  170. StopTimer();
  171. return retval;
  172. }
  173. catch (Exception e)
  174. {
  175. HandleException(e);
  176. throw;
  177. }
  178. }
  179. public override int ReadByte()
  180. {
  181. try
  182. {
  183. StartTimer(IOKind.Read);
  184. int retval = baseStream.ReadByte();
  185. StopTimer();
  186. return retval;
  187. }
  188. catch (Exception e)
  189. {
  190. HandleException(e);
  191. throw;
  192. }
  193. }
  194. public override long Seek(long offset, SeekOrigin origin)
  195. {
  196. return baseStream.Seek(offset, origin);
  197. }
  198. public override void SetLength(long value)
  199. {
  200. baseStream.SetLength(value);
  201. }
  202. public override void Write(byte[] buffer, int offset, int count)
  203. {
  204. try
  205. {
  206. StartTimer(IOKind.Write);
  207. baseStream.Write(buffer, offset, count);
  208. StopTimer();
  209. }
  210. catch (Exception e)
  211. {
  212. HandleException(e);
  213. throw;
  214. }
  215. }
  216. public override bool CanTimeout
  217. {
  218. get { return baseStream.CanTimeout; }
  219. }
  220. public override int ReadTimeout
  221. {
  222. get { return baseStream.ReadTimeout; }
  223. set { baseStream.ReadTimeout = value; }
  224. }
  225. public override int WriteTimeout
  226. {
  227. get { return baseStream.WriteTimeout; }
  228. set { baseStream.WriteTimeout = value; }
  229. }
  230. public override void Close()
  231. {
  232. if (isClosed)
  233. return;
  234. isClosed = true;
  235. baseStream.Close();
  236. baseStream.Dispose();
  237. }
  238. public void ResetTimeout(int newTimeout)
  239. {
  240. if (newTimeout == System.Threading.Timeout.Infinite || newTimeout == 0)
  241. timeout = System.Threading.Timeout.Infinite;
  242. else
  243. timeout = newTimeout;
  244. stopwatch.Reset();
  245. }
  246. /// <summary>
  247. /// Common handler for IO exceptions.
  248. /// Resets timeout to infinity if timeout exception is
  249. /// detected and stops the times.
  250. /// </summary>
  251. /// <param name="e">original exception</param>
  252. void HandleException(Exception e)
  253. {
  254. stopwatch.Stop();
  255. ResetTimeout(-1);
  256. }
  257. }
  258. }
  259. #endif