TransactionBehavior.cs 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.Runtime;
  11. using System.Runtime.CompilerServices;
  12. using System.ServiceModel;
  13. using System.ServiceModel.Activation;
  14. using System.ServiceModel.Channels;
  15. using System.ServiceModel.Diagnostics;
  16. using System.ServiceModel.Diagnostics.Application;
  17. using System.Transactions;
  18. class TransactionBehavior
  19. {
  20. bool isConcurrent;
  21. IsolationLevel isolation = ServiceBehaviorAttribute.DefaultIsolationLevel;
  22. DispatchRuntime dispatch;
  23. TimeSpan timeout = TimeSpan.Zero;
  24. bool isTransactedReceiveChannelDispatcher = false;
  25. internal TransactionBehavior()
  26. {
  27. }
  28. internal TransactionBehavior(DispatchRuntime dispatch)
  29. {
  30. this.isConcurrent = (dispatch.ConcurrencyMode == ConcurrencyMode.Multiple ||
  31. dispatch.ConcurrencyMode == ConcurrencyMode.Reentrant);
  32. this.dispatch = dispatch;
  33. this.isTransactedReceiveChannelDispatcher = dispatch.ChannelDispatcher.IsTransactedReceive;
  34. // Don't pull in System.Transactions.dll if we don't need it
  35. if (dispatch.ChannelDispatcher.TransactionIsolationLevelSet)
  36. {
  37. this.InitializeIsolationLevel(dispatch);
  38. }
  39. this.timeout = TransactionBehavior.NormalizeTimeout(dispatch.ChannelDispatcher.TransactionTimeout);
  40. }
  41. internal static Exception CreateFault(string reasonText, string codeString, bool isNetDispatcherFault)
  42. {
  43. string faultCodeNamespace, action;
  44. // 'Transactions' action should be used only when we expect to have a TransactionChannel in the channel stack
  45. // otherwise one should use the NetDispatch action.
  46. if (isNetDispatcherFault)
  47. {
  48. faultCodeNamespace = FaultCodeConstants.Namespaces.NetDispatch;
  49. action = FaultCodeConstants.Actions.NetDispatcher;
  50. }
  51. else
  52. {
  53. faultCodeNamespace = FaultCodeConstants.Namespaces.Transactions;
  54. action = FaultCodeConstants.Actions.Transactions;
  55. }
  56. FaultReason reason = new FaultReason(reasonText, CultureInfo.CurrentCulture);
  57. FaultCode code = FaultCode.CreateSenderFaultCode(codeString, faultCodeNamespace);
  58. return new FaultException(reason, code, action);
  59. }
  60. internal static TransactionBehavior CreateIfNeeded(DispatchRuntime dispatch)
  61. {
  62. if (TransactionBehavior.NeedsTransactionBehavior(dispatch))
  63. {
  64. return new TransactionBehavior(dispatch);
  65. }
  66. else
  67. {
  68. return null;
  69. }
  70. }
  71. internal static TimeSpan NormalizeTimeout(TimeSpan timeout)
  72. {
  73. if (TimeSpan.Zero == timeout)
  74. {
  75. timeout = TransactionManager.DefaultTimeout;
  76. }
  77. else if (TimeSpan.Zero != TransactionManager.MaximumTimeout && timeout > TransactionManager.MaximumTimeout)
  78. {
  79. timeout = TransactionManager.MaximumTimeout;
  80. }
  81. return timeout;
  82. }
  83. internal static CommittableTransaction CreateTransaction(IsolationLevel isolation, TimeSpan timeout)
  84. {
  85. TransactionOptions options = new TransactionOptions();
  86. options.IsolationLevel = isolation;
  87. options.Timeout = timeout;
  88. return new CommittableTransaction(options);
  89. }
  90. internal void SetCurrent(ref MessageRpc rpc)
  91. {
  92. if (!this.isConcurrent)
  93. {
  94. rpc.InstanceContext.Transaction.SetCurrent(ref rpc);
  95. }
  96. }
  97. internal void ResolveOutcome(ref MessageRpc rpc)
  98. {
  99. if ((rpc.InstanceContext != null) && (rpc.transaction != null))
  100. {
  101. TransactionInstanceContextFacet context = rpc.InstanceContext.Transaction;
  102. if (context != null)
  103. {
  104. context.CheckIfTxCompletedAndUpdateAttached(ref rpc, this.isConcurrent);
  105. }
  106. rpc.Transaction.Complete(rpc.Error);
  107. }
  108. }
  109. Transaction GetInstanceContextTransaction(ref MessageRpc rpc)
  110. {
  111. return rpc.InstanceContext.Transaction.Attached;
  112. }
  113. [MethodImpl(MethodImplOptions.NoInlining)]
  114. void InitializeIsolationLevel(DispatchRuntime dispatch)
  115. {
  116. this.isolation = dispatch.ChannelDispatcher.TransactionIsolationLevel;
  117. }
  118. static bool NeedsTransactionBehavior(DispatchRuntime dispatch)
  119. {
  120. DispatchOperation unhandled = dispatch.UnhandledDispatchOperation;
  121. if ((unhandled != null) && (unhandled.TransactionRequired))
  122. {
  123. return true;
  124. }
  125. if (dispatch.ChannelDispatcher.IsTransactedReceive) //check if we have transacted receive
  126. {
  127. return true;
  128. }
  129. for (int i = 0; i < dispatch.Operations.Count; i++)
  130. {
  131. DispatchOperation operation = dispatch.Operations[i];
  132. if (operation.TransactionRequired)
  133. {
  134. return true;
  135. }
  136. }
  137. return false;
  138. }
  139. internal void ResolveTransaction(ref MessageRpc rpc)
  140. {
  141. if (rpc.Operation.HasDefaultUnhandledActionInvoker)
  142. {
  143. // we ignore unhandled operations
  144. return;
  145. }
  146. Transaction contextTransaction = null;
  147. //If we are inside a TransactedReceiveScope in workflow, then we need to look into the PPD and not the InstanceContext
  148. //to get the contextTransaction
  149. if (rpc.Operation.IsInsideTransactedReceiveScope)
  150. {
  151. // We may want to use an existing transaction for the instance.
  152. IInstanceTransaction instanceTransaction = rpc.Operation.Invoker as IInstanceTransaction;
  153. if (instanceTransaction != null)
  154. {
  155. contextTransaction = instanceTransaction.GetTransactionForInstance(rpc.OperationContext);
  156. }
  157. if (contextTransaction != null)
  158. {
  159. if (DiagnosticUtility.ShouldTraceInformation)
  160. {
  161. TraceUtility.TraceEvent(TraceEventType.Information,
  162. TraceCode.TxSourceTxScopeRequiredUsingExistingTransaction,
  163. SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredUsingExistingTransaction,
  164. contextTransaction.TransactionInformation.LocalIdentifier,
  165. rpc.Operation.Name)
  166. );
  167. }
  168. }
  169. }
  170. else
  171. {
  172. contextTransaction = this.GetInstanceContextTransaction(ref rpc);
  173. }
  174. Transaction transaction = null;
  175. try
  176. {
  177. transaction = TransactionMessageProperty.TryGetTransaction(rpc.Request);
  178. }
  179. catch (TransactionException e)
  180. {
  181. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  182. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionUnmarshalFailed, e.Message), FaultCodeConstants.Codes.TransactionUnmarshalingFailed, false));
  183. }
  184. if (rpc.Operation.TransactionRequired)
  185. {
  186. if (transaction != null)
  187. {
  188. if (this.isTransactedReceiveChannelDispatcher)
  189. {
  190. if (DiagnosticUtility.ShouldTraceInformation)
  191. {
  192. TraceUtility.TraceEvent(TraceEventType.Information,
  193. TraceCode.TxSourceTxScopeRequiredIsTransactedTransport,
  194. SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsTransactedTransport,
  195. transaction.TransactionInformation.LocalIdentifier,
  196. rpc.Operation.Name)
  197. );
  198. }
  199. }
  200. else
  201. {
  202. if (DiagnosticUtility.ShouldTraceInformation)
  203. {
  204. TraceUtility.TraceEvent(TraceEventType.Information,
  205. TraceCode.TxSourceTxScopeRequiredIsTransactionFlow,
  206. SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsTransactionFlow,
  207. transaction.TransactionInformation.LocalIdentifier,
  208. rpc.Operation.Name)
  209. );
  210. }
  211. if (PerformanceCounters.PerformanceCountersEnabled)
  212. {
  213. PerformanceCounters.TxFlowed(PerformanceCounters.GetEndpointDispatcher(), rpc.Operation.Name);
  214. }
  215. bool sameTransaction = false;
  216. if (rpc.Operation.IsInsideTransactedReceiveScope)
  217. {
  218. sameTransaction = transaction.Equals(contextTransaction);
  219. }
  220. else
  221. {
  222. sameTransaction = transaction == contextTransaction;
  223. }
  224. if (!sameTransaction)
  225. {
  226. try
  227. {
  228. transaction = transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete);
  229. }
  230. catch (TransactionException e)
  231. {
  232. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  233. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  234. }
  235. }
  236. }
  237. }
  238. }
  239. else
  240. {
  241. // We got a transaction from the ChannelHandler.
  242. // Transport is transacted.
  243. // But operation doesn't require the transaction, so no one ever will commit it.
  244. // Because of that we have to commit it here.
  245. if (transaction != null && this.isTransactedReceiveChannelDispatcher)
  246. {
  247. try
  248. {
  249. if (null != rpc.TransactedBatchContext)
  250. {
  251. rpc.TransactedBatchContext.ForceCommit();
  252. rpc.TransactedBatchContext = null;
  253. }
  254. else
  255. {
  256. TransactionInstanceContextFacet.Complete(transaction, null);
  257. }
  258. }
  259. finally
  260. {
  261. transaction.Dispose();
  262. transaction = null;
  263. }
  264. }
  265. }
  266. InstanceContext context = rpc.InstanceContext;
  267. if (context.Transaction.ShouldReleaseInstance && !this.isConcurrent)
  268. {
  269. if (context.Behavior.ReleaseServiceInstanceOnTransactionComplete)
  270. {
  271. context.ReleaseServiceInstance();
  272. if (DiagnosticUtility.ShouldTraceInformation)
  273. {
  274. TraceUtility.TraceEvent(TraceEventType.Information,
  275. TraceCode.TxReleaseServiceInstanceOnCompletion,
  276. SR.GetString(SR.TraceCodeTxReleaseServiceInstanceOnCompletion,
  277. contextTransaction.TransactionInformation.LocalIdentifier)
  278. );
  279. }
  280. }
  281. context.Transaction.ShouldReleaseInstance = false;
  282. if (transaction == null || transaction == contextTransaction)
  283. {
  284. rpc.Transaction.Current = contextTransaction;
  285. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  286. }
  287. else
  288. {
  289. contextTransaction = null;
  290. }
  291. }
  292. if (rpc.Operation.TransactionRequired)
  293. {
  294. if (transaction == null)
  295. {
  296. if (contextTransaction != null)
  297. {
  298. transaction = contextTransaction;
  299. if (DiagnosticUtility.ShouldTraceInformation)
  300. {
  301. TraceUtility.TraceEvent(TraceEventType.Information,
  302. TraceCode.TxSourceTxScopeRequiredIsAttachedTransaction,
  303. SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsAttachedTransaction,
  304. transaction.TransactionInformation.LocalIdentifier,
  305. rpc.Operation.Name)
  306. );
  307. }
  308. }
  309. else
  310. {
  311. transaction = TransactionBehavior.CreateTransaction(this.isolation, this.timeout);
  312. if (DiagnosticUtility.ShouldTraceInformation)
  313. {
  314. TraceUtility.TraceEvent(TraceEventType.Information,
  315. TraceCode.TxSourceTxScopeRequiredIsCreateNewTransaction,
  316. SR.GetString(SR.TraceCodeTxSourceTxScopeRequiredIsCreateNewTransaction,
  317. transaction.TransactionInformation.LocalIdentifier,
  318. rpc.Operation.Name)
  319. );
  320. }
  321. }
  322. }
  323. if ((this.isolation != IsolationLevel.Unspecified) && (transaction.IsolationLevel != this.isolation))
  324. {
  325. throw TraceUtility.ThrowHelperError(TransactionBehavior.CreateFault
  326. (SR.GetString(SR.IsolationLevelMismatch2, transaction.IsolationLevel, this.isolation), FaultCodeConstants.Codes.TransactionIsolationLevelMismatch, false), rpc.Request);
  327. }
  328. rpc.Transaction.Current = transaction;
  329. rpc.InstanceContext.Transaction.AddReference(ref rpc, rpc.Transaction.Current, true);
  330. try
  331. {
  332. rpc.Transaction.Clone = transaction.Clone();
  333. if (rpc.Operation.IsInsideTransactedReceiveScope)
  334. {
  335. //It is because we want to synchronize the dispatcher processing of messages with the commit
  336. //processing that is started by the completion of a TransactedReceiveScope. We need to make sure
  337. //that all the dispatcher processing is done and we can do that by creating a blocking dependent clone and only
  338. //completing it after all of the message processing is done for a given TransactionRpcFacet
  339. rpc.Transaction.CreateDependentClone();
  340. }
  341. }
  342. catch (ObjectDisposedException e)//transaction may be async aborted
  343. {
  344. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  345. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  346. }
  347. rpc.InstanceContext.Transaction.AddReference(ref rpc, rpc.Transaction.Clone, false);
  348. rpc.OperationContext.TransactionFacet = rpc.Transaction;
  349. if (!rpc.Operation.TransactionAutoComplete)
  350. {
  351. rpc.Transaction.SetIncomplete();
  352. }
  353. }
  354. }
  355. internal void InitializeCallContext(ref MessageRpc rpc)
  356. {
  357. if (rpc.Operation.TransactionRequired)
  358. {
  359. rpc.Transaction.ThreadEnter(ref rpc.Error);
  360. }
  361. }
  362. internal void ClearCallContext(ref MessageRpc rpc)
  363. {
  364. if (rpc.Operation.TransactionRequired)
  365. {
  366. rpc.Transaction.ThreadLeave();
  367. }
  368. }
  369. }
  370. internal class TransactionRpcFacet
  371. {
  372. //internal members
  373. // Current is the original transaction that we created/flowed/whatever. This is
  374. // the "current" transaction used by the operation, and we keep it around so we
  375. // can commit it, complete it, etc.
  376. //
  377. // Clone is a clone of Current. We keep it around to pass into TransactionScope
  378. // so that System.Transactions.Transaction.Current is not CommittableTransaction
  379. // or anything dangerous like that.
  380. internal Transaction Current;
  381. internal Transaction Clone;
  382. internal DependentTransaction dependentClone;
  383. internal bool IsCompleted = true;
  384. internal MessageRpc rpc;
  385. TransactionScope scope;
  386. bool transactionSetComplete = false; // To track if user has called SetTransactionComplete()
  387. internal TransactionRpcFacet()
  388. {
  389. }
  390. internal TransactionRpcFacet(ref MessageRpc rpc)
  391. {
  392. this.rpc = rpc;
  393. }
  394. // Calling Complete will Commit or Abort the transaction based on,
  395. // error - If any user error is propagated to the service we abort the transaction unless SetTransactionComplete was successful.
  396. // transactionDoomed - If internal error occurred and this error may or may not be propagated
  397. // by the user to the service. Abort the Tx if transactionDoomed is set true.
  398. //
  399. // If the user violates the following rules, the transaction is doomed.
  400. // User cannot call TransactionSetComplete() when TransactionAutoComplete is true.
  401. // User cannot call TransactionSetComplete() multiple times.
  402. [MethodImpl(MethodImplOptions.NoInlining)]
  403. internal void Complete(Exception error)
  404. {
  405. if (!object.ReferenceEquals(this.Current, null))
  406. {
  407. TransactedBatchContext batchContext = this.rpc.TransactedBatchContext;
  408. if (null != batchContext)
  409. {
  410. if (null == error)
  411. {
  412. batchContext.Complete();
  413. }
  414. else
  415. {
  416. batchContext.ForceRollback();
  417. }
  418. batchContext.InDispatch = false;
  419. }
  420. else
  421. {
  422. if (this.transactionSetComplete)
  423. {
  424. // Commit the transaction when TransactionSetComplete() is called and
  425. // even when an exception(non transactional) happens after this call.
  426. rpc.InstanceContext.Transaction.CompletePendingTransaction(this.Current, null);
  427. if (DiagnosticUtility.ShouldTraceInformation)
  428. {
  429. TraceUtility.TraceEvent(TraceEventType.Information,
  430. TraceCode.TxCompletionStatusCompletedForSetComplete,
  431. SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForSetComplete,
  432. this.Current.TransactionInformation.LocalIdentifier,
  433. this.rpc.Operation.Name)
  434. );
  435. }
  436. }
  437. else if (this.IsCompleted || (error != null))
  438. {
  439. rpc.InstanceContext.Transaction.CompletePendingTransaction(this.Current, error);
  440. }
  441. }
  442. if (this.rpc.Operation.IsInsideTransactedReceiveScope)
  443. {
  444. //We are done with the message processing associated with this TransactionRpcFacet so a commit that may have
  445. //been started by a TransactedReceiveScope can move forward.
  446. this.CompleteDependentClone();
  447. }
  448. this.Current = null;
  449. }
  450. }
  451. internal void SetIncomplete()
  452. {
  453. this.IsCompleted = false;
  454. }
  455. internal void Completed()
  456. {
  457. if (this.scope == null)
  458. {
  459. return;
  460. }
  461. // Prohibit user from calling SetTransactionComplete() when TransactionAutoComplete is set to true.
  462. // Transaction will be aborted.
  463. if (this.rpc.Operation.TransactionAutoComplete)
  464. {
  465. try
  466. {
  467. this.Current.Rollback();
  468. }
  469. catch (ObjectDisposedException e)
  470. {
  471. //we don't want to mask the real error here
  472. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  473. }
  474. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
  475. SR.GetString(SR.SFxTransactionInvalidSetTransactionComplete, rpc.Operation.Name, rpc.Host.Description.Name)));
  476. }
  477. // Prohibit user from calling SetTransactionComplete() multiple times.
  478. // Transaction will be aborted.
  479. else if (this.transactionSetComplete)
  480. {
  481. try
  482. {
  483. this.Current.Rollback();
  484. }
  485. catch (ObjectDisposedException e)
  486. {
  487. //we don't want to mask the real error here
  488. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  489. }
  490. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
  491. SR.GetString(SR.SFxMultiSetTransactionComplete, rpc.Operation.Name, rpc.Host.Description.Name)));
  492. }
  493. this.transactionSetComplete = true;
  494. this.IsCompleted = true;
  495. this.scope.Complete();
  496. }
  497. [MethodImpl(MethodImplOptions.NoInlining)]
  498. internal void ThreadEnter(ref Exception error)
  499. {
  500. Transaction clone = this.Clone;
  501. if ((clone != null) && (error == null))
  502. {
  503. if (TD.TransactionScopeCreateIsEnabled())
  504. {
  505. if (clone != null && clone.TransactionInformation != null)
  506. {
  507. TD.TransactionScopeCreate(rpc.EventTraceActivity,
  508. clone.TransactionInformation.LocalIdentifier,
  509. clone.TransactionInformation.DistributedIdentifier);
  510. }
  511. }
  512. this.scope = this.rpc.InstanceContext.Transaction.CreateTransactionScope(clone);
  513. this.transactionSetComplete = false;
  514. }
  515. }
  516. [MethodImpl(MethodImplOptions.NoInlining)]
  517. internal void ThreadLeave()
  518. {
  519. if (this.scope != null)
  520. {
  521. if (!this.transactionSetComplete)
  522. {
  523. this.scope.Complete();
  524. }
  525. try
  526. {
  527. this.scope.Dispose();
  528. this.scope = null;
  529. }
  530. catch (TransactionException e)
  531. {
  532. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  533. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  534. }
  535. }
  536. }
  537. internal void CreateDependentClone()
  538. {
  539. if ((this.dependentClone == null) && (this.Clone != null))
  540. {
  541. this.dependentClone = this.Clone.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
  542. }
  543. }
  544. internal void CompleteDependentClone()
  545. {
  546. if (this.dependentClone != null)
  547. {
  548. this.dependentClone.Complete();
  549. }
  550. }
  551. }
  552. internal sealed class TransactionInstanceContextFacet
  553. {
  554. internal Transaction waiting; // waiting to become Single because Single is on his way out.
  555. internal Transaction Attached;
  556. IResumeMessageRpc paused; // the IResumeMessageRpc for this.waiting.
  557. object mutex;
  558. Transaction current; // the one true transaction when Concurrency=false.
  559. InstanceContext instanceContext;
  560. Dictionary<Transaction, RemoveReferenceRM> pending; // When Concurrency=true, all the still pending guys.
  561. bool shouldReleaseInstance = false;
  562. internal TransactionInstanceContextFacet(InstanceContext instanceContext)
  563. {
  564. this.instanceContext = instanceContext;
  565. this.mutex = instanceContext.ThisLock;
  566. }
  567. // ........................................................................................................
  568. // no need to lock the following property because it's used only if Concurrency = false
  569. internal bool ShouldReleaseInstance
  570. {
  571. get
  572. {
  573. return this.shouldReleaseInstance;
  574. }
  575. set
  576. {
  577. this.shouldReleaseInstance = value;
  578. }
  579. }
  580. // ........................................................................................................
  581. [MethodImpl(MethodImplOptions.NoInlining)]
  582. internal void CheckIfTxCompletedAndUpdateAttached(ref MessageRpc rpc, bool isConcurrent)
  583. {
  584. if (rpc.Transaction.Current == null)
  585. {
  586. return;
  587. }
  588. lock (this.mutex)
  589. {
  590. if (!isConcurrent)
  591. {
  592. if (this.shouldReleaseInstance)
  593. {
  594. this.shouldReleaseInstance = false;
  595. if (rpc.Error == null) //we don't want to mask the initial error
  596. {
  597. rpc.Error = TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true);
  598. DiagnosticUtility.TraceHandledException(rpc.Error, TraceEventType.Error);
  599. if (DiagnosticUtility.ShouldTraceInformation)
  600. {
  601. TraceUtility.TraceEvent(TraceEventType.Information,
  602. TraceCode.TxCompletionStatusCompletedForAsyncAbort,
  603. SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAsyncAbort,
  604. rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
  605. rpc.Operation.Name)
  606. );
  607. }
  608. }
  609. }
  610. if (rpc.Transaction.IsCompleted || (rpc.Error != null))
  611. {
  612. if (DiagnosticUtility.ShouldTraceInformation)
  613. {
  614. if (rpc.Error != null)
  615. {
  616. TraceUtility.TraceEvent(TraceEventType.Information,
  617. TraceCode.TxCompletionStatusCompletedForError,
  618. SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForError,
  619. rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
  620. rpc.Operation.Name)
  621. );
  622. }
  623. else
  624. {
  625. TraceUtility.TraceEvent(TraceEventType.Information,
  626. TraceCode.TxCompletionStatusCompletedForAutocomplete,
  627. SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAutocomplete,
  628. rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
  629. rpc.Operation.Name)
  630. );
  631. }
  632. }
  633. this.Attached = null;
  634. if (!(waiting == null))
  635. {
  636. // tx processing requires failfast when state is inconsistent
  637. DiagnosticUtility.FailFast("waiting should be null when resetting current");
  638. }
  639. this.current = null;
  640. }
  641. else
  642. {
  643. this.Attached = rpc.Transaction.Current;
  644. if (DiagnosticUtility.ShouldTraceInformation)
  645. {
  646. TraceUtility.TraceEvent(TraceEventType.Information,
  647. TraceCode.TxCompletionStatusRemainsAttached,
  648. SR.GetString(SR.TraceCodeTxCompletionStatusRemainsAttached,
  649. rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
  650. rpc.Operation.Name)
  651. );
  652. }
  653. }
  654. }
  655. else if (!this.pending.ContainsKey(rpc.Transaction.Current))
  656. {
  657. //transaction has been asynchronously aborted
  658. if (rpc.Error == null) //we don't want to mask the initial error
  659. {
  660. rpc.Error = TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true);
  661. DiagnosticUtility.TraceHandledException(rpc.Error, TraceEventType.Error);
  662. if (DiagnosticUtility.ShouldTraceInformation)
  663. {
  664. TraceUtility.TraceEvent(TraceEventType.Information,
  665. TraceCode.TxCompletionStatusCompletedForAsyncAbort,
  666. SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForAsyncAbort,
  667. rpc.Transaction.Current.TransactionInformation.LocalIdentifier,
  668. rpc.Operation.Name)
  669. );
  670. }
  671. }
  672. }
  673. }
  674. }
  675. // ........................................................................................................
  676. internal void CompletePendingTransaction(Transaction transaction, Exception error)
  677. {
  678. lock (this.mutex)
  679. {
  680. if (this.pending.ContainsKey(transaction))
  681. {
  682. Complete(transaction, error);
  683. }
  684. }
  685. }
  686. // ........................................................................................................
  687. internal static void Complete(Transaction transaction, Exception error)
  688. {
  689. try
  690. {
  691. if (error == null)
  692. {
  693. CommittableTransaction commit = (transaction as CommittableTransaction);
  694. if (commit != null)
  695. {
  696. commit.Commit();
  697. }
  698. else
  699. {
  700. DependentTransaction complete = (transaction as DependentTransaction);
  701. if (complete != null)
  702. {
  703. complete.Complete();
  704. }
  705. }
  706. }
  707. else
  708. {
  709. transaction.Rollback();
  710. }
  711. }
  712. catch (TransactionException e)
  713. {
  714. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  715. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  716. }
  717. }
  718. // ........................................................................................................
  719. internal TransactionScope CreateTransactionScope(Transaction transaction)
  720. {
  721. lock (this.mutex)
  722. {
  723. if (this.pending.ContainsKey(transaction))
  724. {
  725. try
  726. {
  727. return new TransactionScope(transaction);
  728. }
  729. catch (TransactionException e)
  730. {
  731. DiagnosticUtility.TraceHandledException(e, TraceEventType.Error);
  732. //we'll rethrow below
  733. }
  734. }
  735. }
  736. //the transaction was asynchronously aborted
  737. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TransactionBehavior.CreateFault(SR.GetString(SR.SFxTransactionAsyncAborted), FaultCodeConstants.Codes.TransactionAborted, true));
  738. }
  739. // ........................................................................................................
  740. internal void SetCurrent(ref MessageRpc rpc)
  741. {
  742. Transaction requestTransaction = rpc.Transaction.Current;
  743. if (!(requestTransaction != null))
  744. {
  745. // tx processing requires failfast when state is inconsistent
  746. DiagnosticUtility.FailFast("we should never get here with a requestTransaction null");
  747. }
  748. lock (this.mutex)
  749. {
  750. if (this.current == null)
  751. {
  752. this.current = requestTransaction;
  753. }
  754. else if (this.current != requestTransaction)
  755. {
  756. this.waiting = requestTransaction;
  757. this.paused = rpc.Pause();
  758. }
  759. else
  760. {
  761. rpc.Transaction.Current = this.current; //rpc.Transaction.Current should get the dependent clone
  762. }
  763. }
  764. }
  765. // ........................................................................................................
  766. internal void AddReference(ref MessageRpc rpc, Transaction tx, bool updateCallCount)
  767. {
  768. lock (this.mutex)
  769. {
  770. if (this.pending == null)
  771. {
  772. this.pending = new Dictionary<Transaction, RemoveReferenceRM>();
  773. }
  774. if (tx != null)
  775. {
  776. if (this.pending == null)
  777. {
  778. this.pending = new Dictionary<Transaction, RemoveReferenceRM>();
  779. }
  780. RemoveReferenceRM rm;
  781. if (!this.pending.TryGetValue(tx, out rm))
  782. {
  783. RemoveReferenceRM rrm = new RemoveReferenceRM(this.instanceContext, tx, rpc.Operation.Name);
  784. rrm.CallCount = 1;
  785. this.pending.Add(tx, rrm);
  786. }
  787. else if (updateCallCount)
  788. {
  789. rm.CallCount += 1;
  790. }
  791. }
  792. }
  793. }
  794. internal void RemoveReference(Transaction tx)
  795. {
  796. lock (this.mutex)
  797. {
  798. if (tx.Equals(this.current))
  799. {
  800. if (this.waiting != null)
  801. {
  802. this.current = waiting;
  803. this.waiting = null;
  804. if (instanceContext.Behavior.ReleaseServiceInstanceOnTransactionComplete)
  805. {
  806. instanceContext.ReleaseServiceInstance();
  807. if (DiagnosticUtility.ShouldTraceInformation)
  808. {
  809. TraceUtility.TraceEvent(TraceEventType.Information,
  810. TraceCode.TxReleaseServiceInstanceOnCompletion,
  811. SR.GetString(SR.TraceCodeTxReleaseServiceInstanceOnCompletion,
  812. tx.TransactionInformation.LocalIdentifier)
  813. );
  814. }
  815. }
  816. bool alreadyResumedNoLock;
  817. this.paused.Resume(out alreadyResumedNoLock);
  818. if (alreadyResumedNoLock)
  819. {
  820. Fx.Assert("TransactionBehavior resumed more than once for same call.");
  821. }
  822. }
  823. else
  824. {
  825. this.shouldReleaseInstance = true;
  826. this.current = null;
  827. }
  828. }
  829. if (this.pending != null)
  830. {
  831. if (this.pending.ContainsKey(tx))
  832. {
  833. this.pending.Remove(tx);
  834. }
  835. }
  836. }
  837. }
  838. // ........................................................................................................
  839. abstract class VolatileBase : ISinglePhaseNotification
  840. {
  841. protected InstanceContext InstanceContext;
  842. protected Transaction Transaction;
  843. protected VolatileBase(InstanceContext instanceContext, Transaction transaction)
  844. {
  845. this.InstanceContext = instanceContext;
  846. this.Transaction = transaction;
  847. this.Transaction.EnlistVolatile(this, EnlistmentOptions.None);
  848. }
  849. protected abstract void Completed();
  850. public virtual void Commit(Enlistment enlistment)
  851. {
  852. this.Completed();
  853. }
  854. public virtual void InDoubt(Enlistment enlistment)
  855. {
  856. this.Completed();
  857. }
  858. public virtual void Rollback(Enlistment enlistment)
  859. {
  860. this.Completed();
  861. }
  862. public virtual void SinglePhaseCommit(SinglePhaseEnlistment enlistment)
  863. {
  864. enlistment.Committed();
  865. this.Completed();
  866. }
  867. public void Prepare(PreparingEnlistment preparingEnlistment)
  868. {
  869. preparingEnlistment.Prepared();
  870. }
  871. }
  872. sealed class RemoveReferenceRM : VolatileBase
  873. {
  874. string operation;
  875. long callCount = 0;
  876. EndpointDispatcher endpointDispatcher;
  877. internal RemoveReferenceRM(InstanceContext instanceContext, Transaction tx, string operation)
  878. : base(instanceContext, tx)
  879. {
  880. this.operation = operation;
  881. if (PerformanceCounters.PerformanceCountersEnabled)
  882. {
  883. this.endpointDispatcher = PerformanceCounters.GetEndpointDispatcher();
  884. }
  885. AspNetEnvironment.Current.IncrementBusyCount();
  886. if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
  887. {
  888. AspNetEnvironment.Current.TraceIncrementBusyCount(this.GetType().FullName);
  889. }
  890. }
  891. internal long CallCount
  892. {
  893. get { return this.callCount; }
  894. set { this.callCount = value; }
  895. }
  896. protected override void Completed()
  897. {
  898. this.InstanceContext.Transaction.RemoveReference(this.Transaction);
  899. AspNetEnvironment.Current.DecrementBusyCount();
  900. if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
  901. {
  902. AspNetEnvironment.Current.TraceDecrementBusyCount(this.GetType().FullName);
  903. }
  904. }
  905. public override void SinglePhaseCommit(SinglePhaseEnlistment enlistment)
  906. {
  907. if (PerformanceCounters.PerformanceCountersEnabled)
  908. {
  909. PerformanceCounters.TxCommitted(this.endpointDispatcher, CallCount);
  910. }
  911. base.SinglePhaseCommit(enlistment);
  912. }
  913. public override void Commit(Enlistment enlistment)
  914. {
  915. if (PerformanceCounters.PerformanceCountersEnabled)
  916. {
  917. PerformanceCounters.TxCommitted(this.endpointDispatcher, CallCount);
  918. }
  919. base.Commit(enlistment);
  920. }
  921. public override void Rollback(Enlistment enlistment)
  922. {
  923. if (PerformanceCounters.PerformanceCountersEnabled)
  924. {
  925. PerformanceCounters.TxAborted(this.endpointDispatcher, CallCount);
  926. }
  927. if (DiagnosticUtility.ShouldTraceInformation)
  928. {
  929. TraceUtility.TraceEvent(TraceEventType.Information,
  930. TraceCode.TxAsyncAbort,
  931. SR.GetString(SR.TraceCodeTxAsyncAbort,
  932. this.Transaction.TransactionInformation.LocalIdentifier)
  933. );
  934. }
  935. base.Rollback(enlistment);
  936. }
  937. public override void InDoubt(Enlistment enlistment)
  938. {
  939. if (PerformanceCounters.PerformanceCountersEnabled)
  940. {
  941. PerformanceCounters.TxInDoubt(this.endpointDispatcher, CallCount);
  942. }
  943. base.InDoubt(enlistment);
  944. }
  945. }
  946. }
  947. internal enum ExclusiveInstanceContextTransactionResult
  948. {
  949. Acquired, Wait, Fault
  950. };
  951. }