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.

366 lines
10 KiB

4 years ago
  1. #if MYSQL_6_9
  2. // Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. 2014, Oracle and/or its affiliates. All rights reserved.
  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.Runtime.InteropServices;
  25. using System.Threading;
  26. using System.IO;
  27. using Externals.MySql.Data.MySqlClient;
  28. using System.Diagnostics;
  29. namespace Externals.MySql.Data.Common
  30. {
  31. /// <summary>
  32. /// Helper class to encapsulate shared memory functionality
  33. /// Also cares of proper cleanup of file mapping object and cew
  34. /// </summary>
  35. internal class SharedMemory : IDisposable
  36. {
  37. private const uint FILE_MAP_WRITE = 0x0002;
  38. IntPtr fileMapping;
  39. IntPtr view;
  40. public SharedMemory(string name, IntPtr size)
  41. {
  42. fileMapping = NativeMethods.OpenFileMapping(FILE_MAP_WRITE, false,
  43. name);
  44. if (fileMapping == IntPtr.Zero)
  45. {
  46. throw new MySqlException("Cannot open file mapping " + name);
  47. }
  48. view = NativeMethods.MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, size);
  49. }
  50. #region Destructor
  51. ~SharedMemory()
  52. {
  53. Dispose(false);
  54. }
  55. #endregion
  56. public IntPtr View
  57. {
  58. get { return view; }
  59. }
  60. public void Dispose()
  61. {
  62. Dispose(true);
  63. GC.SuppressFinalize(this);
  64. }
  65. protected virtual void Dispose(bool disposing)
  66. {
  67. if (disposing)
  68. {
  69. if (view != IntPtr.Zero)
  70. {
  71. NativeMethods.UnmapViewOfFile(view);
  72. view = IntPtr.Zero;
  73. }
  74. if (fileMapping != IntPtr.Zero)
  75. {
  76. // Free the handle
  77. NativeMethods.CloseHandle(fileMapping);
  78. fileMapping = IntPtr.Zero;
  79. }
  80. }
  81. }
  82. }
  83. /// <summary>
  84. /// Summary description for SharedMemoryStream.
  85. /// </summary>
  86. internal class SharedMemoryStream : Stream
  87. {
  88. private string memoryName;
  89. private EventWaitHandle serverRead;
  90. private EventWaitHandle serverWrote;
  91. private EventWaitHandle clientRead;
  92. private EventWaitHandle clientWrote;
  93. private EventWaitHandle connectionClosed;
  94. private SharedMemory data;
  95. private int bytesLeft;
  96. private int position;
  97. private int connectNumber;
  98. private const int BUFFERLENGTH = 16004;
  99. private int readTimeout = System.Threading.Timeout.Infinite;
  100. private int writeTimeout = System.Threading.Timeout.Infinite;
  101. public SharedMemoryStream(string memName)
  102. {
  103. memoryName = memName;
  104. }
  105. public void Open(uint timeOut)
  106. {
  107. if (connectionClosed != null)
  108. {
  109. Debug.Assert(false, "Connection is already open");
  110. }
  111. GetConnectNumber(timeOut);
  112. SetupEvents();
  113. }
  114. public override void Close()
  115. {
  116. if (connectionClosed != null)
  117. {
  118. bool isClosed = connectionClosed.WaitOne(0);
  119. if (!isClosed)
  120. {
  121. connectionClosed.Set();
  122. connectionClosed.Close();
  123. }
  124. connectionClosed = null;
  125. EventWaitHandle[] handles = { serverRead, serverWrote, clientRead, clientWrote };
  126. for (int i = 0; i < handles.Length; i++)
  127. {
  128. if (handles[i] != null)
  129. handles[i].Close();
  130. }
  131. if (data != null)
  132. {
  133. data.Dispose();
  134. data = null;
  135. }
  136. }
  137. }
  138. private void GetConnectNumber(uint timeOut)
  139. {
  140. EventWaitHandle connectRequest;
  141. try
  142. {
  143. connectRequest =
  144. EventWaitHandle.OpenExisting(memoryName + "_CONNECT_REQUEST");
  145. }
  146. catch (Exception)
  147. {
  148. // If server runs as service, its shared memory is global
  149. // And if connector runs in user session, it needs to prefix
  150. // shared memory name with "Global\"
  151. string prefixedMemoryName = @"Global\" + memoryName;
  152. connectRequest =
  153. EventWaitHandle.OpenExisting(prefixedMemoryName + "_CONNECT_REQUEST");
  154. memoryName = prefixedMemoryName;
  155. }
  156. EventWaitHandle connectAnswer =
  157. EventWaitHandle.OpenExisting(memoryName + "_CONNECT_ANSWER");
  158. using (SharedMemory connectData =
  159. new SharedMemory(memoryName + "_CONNECT_DATA", (IntPtr)4))
  160. {
  161. // now start the connection
  162. if (!connectRequest.Set())
  163. throw new MySqlException("Failed to open shared memory connection");
  164. if (!connectAnswer.WaitOne((int)(timeOut * 1000), false))
  165. throw new MySqlException("Timeout during connection");
  166. connectNumber = Marshal.ReadInt32(connectData.View);
  167. }
  168. }
  169. private void SetupEvents()
  170. {
  171. string prefix = memoryName + "_" + connectNumber;
  172. data = new SharedMemory(prefix + "_DATA", (IntPtr)BUFFERLENGTH);
  173. serverWrote = EventWaitHandle.OpenExisting(prefix + "_SERVER_WROTE");
  174. serverRead = EventWaitHandle.OpenExisting(prefix + "_SERVER_READ");
  175. clientWrote = EventWaitHandle.OpenExisting(prefix + "_CLIENT_WROTE");
  176. clientRead = EventWaitHandle.OpenExisting(prefix + "_CLIENT_READ");
  177. connectionClosed = EventWaitHandle.OpenExisting(prefix + "_CONNECTION_CLOSED");
  178. // tell the server we are ready
  179. serverRead.Set();
  180. }
  181. #region Properties
  182. public override bool CanRead
  183. {
  184. get { return true; }
  185. }
  186. public override bool CanSeek
  187. {
  188. get { return false; }
  189. }
  190. public override bool CanWrite
  191. {
  192. get { return true; }
  193. }
  194. public override long Length
  195. {
  196. get { throw new NotSupportedException("SharedMemoryStream does not support seeking - length"); }
  197. }
  198. public override long Position
  199. {
  200. get { throw new NotSupportedException("SharedMemoryStream does not support seeking - position"); }
  201. set { }
  202. }
  203. #endregion
  204. public override void Flush()
  205. {
  206. // No need to flush anything to disk ,as our shared memory is backed
  207. // by the page file
  208. }
  209. public override int Read(byte[] buffer, int offset, int count)
  210. {
  211. int timeLeft = readTimeout;
  212. WaitHandle[] waitHandles = { serverWrote, connectionClosed };
  213. LowResolutionStopwatch stopwatch = new LowResolutionStopwatch();
  214. while (bytesLeft == 0)
  215. {
  216. stopwatch.Start();
  217. int index = WaitHandle.WaitAny(waitHandles, timeLeft);
  218. stopwatch.Stop();
  219. if (index == WaitHandle.WaitTimeout)
  220. throw new TimeoutException("Timeout when reading from shared memory");
  221. if (waitHandles[index] == connectionClosed)
  222. throw new MySqlException("Connection to server lost", true, null);
  223. if (readTimeout != System.Threading.Timeout.Infinite)
  224. {
  225. timeLeft = readTimeout - (int)stopwatch.ElapsedMilliseconds;
  226. if (timeLeft < 0)
  227. throw new TimeoutException("Timeout when reading from shared memory");
  228. }
  229. bytesLeft = Marshal.ReadInt32(data.View);
  230. position = 4;
  231. }
  232. int len = Math.Min(count, bytesLeft);
  233. long baseMem = data.View.ToInt64() + position;
  234. for (int i = 0; i < len; i++, position++)
  235. buffer[offset + i] = Marshal.ReadByte((IntPtr)(baseMem + i));
  236. bytesLeft -= len;
  237. if (bytesLeft == 0)
  238. clientRead.Set();
  239. return len;
  240. }
  241. public override long Seek(long offset, SeekOrigin origin)
  242. {
  243. throw new NotSupportedException("SharedMemoryStream does not support seeking");
  244. }
  245. public override void Write(byte[] buffer, int offset, int count)
  246. {
  247. int leftToDo = count;
  248. int buffPos = offset;
  249. WaitHandle[] waitHandles = { serverRead, connectionClosed };
  250. LowResolutionStopwatch stopwatch = new LowResolutionStopwatch();
  251. int timeLeft = writeTimeout;
  252. while (leftToDo > 0)
  253. {
  254. stopwatch.Start();
  255. int index = WaitHandle.WaitAny(waitHandles, timeLeft);
  256. stopwatch.Stop();
  257. if (waitHandles[index] == connectionClosed)
  258. throw new MySqlException("Connection to server lost", true, null);
  259. if (index == WaitHandle.WaitTimeout)
  260. throw new TimeoutException("Timeout when reading from shared memory");
  261. if (writeTimeout != System.Threading.Timeout.Infinite)
  262. {
  263. timeLeft = writeTimeout - (int)stopwatch.ElapsedMilliseconds;
  264. if (timeLeft < 0)
  265. throw new TimeoutException("Timeout when writing to shared memory");
  266. }
  267. int bytesToDo = Math.Min(leftToDo, BUFFERLENGTH);
  268. long baseMem = data.View.ToInt64() + 4;
  269. Marshal.WriteInt32(data.View, bytesToDo);
  270. Marshal.Copy(buffer, buffPos, (IntPtr)baseMem, bytesToDo);
  271. buffPos += bytesToDo;
  272. leftToDo -= bytesToDo;
  273. if (!clientWrote.Set())
  274. throw new MySqlException("Writing to shared memory failed");
  275. }
  276. }
  277. public override void SetLength(long value)
  278. {
  279. throw new NotSupportedException("SharedMemoryStream does not support seeking");
  280. }
  281. public override bool CanTimeout
  282. {
  283. get
  284. {
  285. return true;
  286. }
  287. }
  288. public override int ReadTimeout
  289. {
  290. get
  291. {
  292. return readTimeout;
  293. }
  294. set
  295. {
  296. readTimeout = value;
  297. }
  298. }
  299. public override int WriteTimeout
  300. {
  301. get
  302. {
  303. return writeTimeout;
  304. }
  305. set
  306. {
  307. writeTimeout = value;
  308. }
  309. }
  310. }
  311. }
  312. #endif