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.

205 lines
6.2 KiB

4 years ago
  1. #if MYSQL_6_9
  2. // Copyright � 2004, 2010, 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.Transactions;
  25. using System.Collections;
  26. using System.Collections.Generic;
  27. using System.Data;
  28. using System.Threading;
  29. namespace Externals.MySql.Data.MySqlClient
  30. {
  31. /// <summary>
  32. /// Represents a single(not nested) TransactionScope
  33. /// </summary>
  34. internal class MySqlTransactionScope
  35. {
  36. public MySqlConnection connection;
  37. public Transaction baseTransaction;
  38. public MySqlTransaction simpleTransaction;
  39. public int rollbackThreadId;
  40. public MySqlTransactionScope(MySqlConnection con, Transaction trans,
  41. MySqlTransaction simpleTransaction)
  42. {
  43. connection = con;
  44. baseTransaction = trans;
  45. this.simpleTransaction = simpleTransaction;
  46. }
  47. public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
  48. {
  49. // prevent commands in main thread to run concurrently
  50. Driver driver = connection.driver;
  51. lock (driver)
  52. {
  53. rollbackThreadId = Thread.CurrentThread.ManagedThreadId;
  54. while (connection.Reader != null)
  55. {
  56. // wait for reader to finish. Maybe we should not wait
  57. // forever and cancel it after some time?
  58. System.Threading.Thread.Sleep(100);
  59. }
  60. simpleTransaction.Rollback();
  61. singlePhaseEnlistment.Aborted();
  62. DriverTransactionManager.RemoveDriverInTransaction(baseTransaction);
  63. driver.CurrentTransaction = null;
  64. if (connection.State == ConnectionState.Closed)
  65. connection.CloseFully();
  66. rollbackThreadId = 0;
  67. }
  68. }
  69. public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
  70. {
  71. simpleTransaction.Commit();
  72. singlePhaseEnlistment.Committed();
  73. DriverTransactionManager.RemoveDriverInTransaction(baseTransaction);
  74. connection.driver.CurrentTransaction = null;
  75. if (connection.State == ConnectionState.Closed)
  76. connection.CloseFully();
  77. }
  78. }
  79. internal sealed class MySqlPromotableTransaction : IPromotableSinglePhaseNotification, ITransactionPromoter
  80. {
  81. // Per-thread stack to manage nested transaction scopes
  82. [ThreadStatic]
  83. static Stack<MySqlTransactionScope> globalScopeStack;
  84. MySqlConnection connection;
  85. Transaction baseTransaction;
  86. Stack<MySqlTransactionScope> scopeStack;
  87. public MySqlPromotableTransaction(MySqlConnection connection, Transaction baseTransaction)
  88. {
  89. this.connection = connection;
  90. this.baseTransaction = baseTransaction;
  91. }
  92. public Transaction BaseTransaction
  93. {
  94. get
  95. {
  96. if (scopeStack.Count > 0)
  97. return scopeStack.Peek().baseTransaction;
  98. else
  99. return null;
  100. }
  101. }
  102. public bool InRollback
  103. {
  104. get
  105. {
  106. if (scopeStack.Count > 0)
  107. {
  108. MySqlTransactionScope currentScope = scopeStack.Peek();
  109. if (currentScope.rollbackThreadId == Thread.CurrentThread.ManagedThreadId)
  110. {
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. }
  117. void IPromotableSinglePhaseNotification.Initialize()
  118. {
  119. string valueName = Enum.GetName(
  120. typeof(System.Transactions.IsolationLevel), baseTransaction.IsolationLevel);
  121. System.Data.IsolationLevel dataLevel = (System.Data.IsolationLevel)Enum.Parse(
  122. typeof(System.Data.IsolationLevel), valueName);
  123. MySqlTransaction simpleTransaction = connection.BeginTransaction(dataLevel);
  124. // We need to save the per-thread scope stack locally.
  125. // We cannot always use thread static variable in rollback: when scope
  126. // times out, rollback is issued by another thread.
  127. if (globalScopeStack == null)
  128. {
  129. globalScopeStack = new Stack<MySqlTransactionScope>();
  130. }
  131. scopeStack = globalScopeStack;
  132. scopeStack.Push(new MySqlTransactionScope(connection, baseTransaction,
  133. simpleTransaction));
  134. }
  135. void IPromotableSinglePhaseNotification.Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
  136. {
  137. MySqlTransactionScope current = scopeStack.Peek();
  138. current.Rollback(singlePhaseEnlistment);
  139. scopeStack.Pop();
  140. }
  141. void IPromotableSinglePhaseNotification.SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
  142. {
  143. scopeStack.Pop().SinglePhaseCommit(singlePhaseEnlistment);
  144. }
  145. byte[] ITransactionPromoter.Promote()
  146. {
  147. throw new NotSupportedException();
  148. }
  149. }
  150. internal class DriverTransactionManager
  151. {
  152. private static Hashtable driversInUse = new Hashtable();
  153. public static Driver GetDriverInTransaction(Transaction transaction)
  154. {
  155. lock (driversInUse.SyncRoot)
  156. {
  157. Driver d = (Driver)driversInUse[transaction.GetHashCode()];
  158. return d;
  159. }
  160. }
  161. public static void SetDriverInTransaction(Driver driver)
  162. {
  163. lock (driversInUse.SyncRoot)
  164. {
  165. driversInUse[driver.CurrentTransaction.BaseTransaction.GetHashCode()] = driver;
  166. }
  167. }
  168. public static void RemoveDriverInTransaction(Transaction transaction)
  169. {
  170. lock (driversInUse.SyncRoot)
  171. {
  172. driversInUse.Remove(transaction.GetHashCode());
  173. }
  174. }
  175. }
  176. }
  177. #endif