| File: System\Transactions\CommittableTransaction.cs | Web Access |
| Project: src\runtime\src\libraries\System.Transactions.Local\src\System.Transactions.Local.csproj (System.Transactions.Local) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Runtime.Versioning; using System.Threading; #pragma warning disable CS1591 namespace System.Transactions { [UnsupportedOSPlatform("browser")] public sealed class CommittableTransaction : Transaction, IAsyncResult { // Create a transaction with defaults public CommittableTransaction() : this(TransactionManager.DefaultIsolationLevel, TransactionManager.DefaultTimeout) { } // Create a transaction with the given info public CommittableTransaction(TimeSpan timeout) : this(TransactionManager.DefaultIsolationLevel, timeout) { } // Create a transaction with the given options public CommittableTransaction(TransactionOptions options) : this(options.IsolationLevel, options.Timeout) { } internal CommittableTransaction(IsolationLevel isoLevel, TimeSpan timeout) : base(isoLevel, (InternalTransaction?)null) { // object to use for synchronization rather than locking on a public object _internalTransaction = new InternalTransaction(timeout, this); // Because we passed null for the internal transaction to the base class, we need to // fill in the traceIdentifier field here. _internalTransaction._cloneCount = 1; _cloneId = 1; TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.TransactionCreated(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } } public IAsyncResult BeginCommit(AsyncCallback? asyncCallback, object? asyncState) { TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); etwLog.TransactionCommit(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } ObjectDisposedException.ThrowIf(Disposed, this); lock (_internalTransaction) { if (_complete) { throw TransactionException.CreateTransactionCompletedException(DistributedTxId); } Debug.Assert(_internalTransaction.State != null); // this.complete will get set to true when the transaction enters a state that is // beyond Phase0. _internalTransaction.State.BeginCommit(_internalTransaction, true, asyncCallback, asyncState); } if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } return this; } // Forward the commit to the state machine to take the appropriate action. // public void Commit() { TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); etwLog.TransactionCommit(TraceSourceType.TraceSourceLtm, TransactionTraceId, "CommittableTransaction"); } ObjectDisposedException.ThrowIf(Disposed, this); lock (_internalTransaction) { if (_complete) { throw TransactionException.CreateTransactionCompletedException(DistributedTxId); } Debug.Assert(_internalTransaction.State != null); _internalTransaction.State.BeginCommit(_internalTransaction, false, null, null); // now that commit has started wait for the monitor on the transaction to know // if the transaction is done. do { if (_internalTransaction.State.IsCompleted(_internalTransaction)) { break; } } while (Monitor.Wait(_internalTransaction)); _internalTransaction.State.EndCommit(_internalTransaction); } if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } } internal override void InternalDispose() { TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); } if (Interlocked.Exchange(ref _disposed, Transaction._disposedTrueValue) == Transaction._disposedTrueValue) { return; } Debug.Assert(_internalTransaction.State != null); if (_internalTransaction.State.get_Status(_internalTransaction) == TransactionStatus.Active) { lock (_internalTransaction) { // Since this is the root transaction do state based dispose. _internalTransaction.State.DisposeRoot(_internalTransaction); } } // Attempt to clean up the internal transaction long remainingITx = Interlocked.Decrement(ref _internalTransaction._cloneCount); if (remainingITx == 0) { _internalTransaction.Dispose(); } if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } } public void EndCommit(IAsyncResult asyncResult) { TransactionsEtwProvider etwLog = TransactionsEtwProvider.Log; if (etwLog.IsEnabled()) { etwLog.MethodEnter(TraceSourceType.TraceSourceLtm, this); } if (asyncResult != ((object)this)) { throw new ArgumentException(SR.BadAsyncResult, nameof(asyncResult)); } lock (_internalTransaction) { do { Debug.Assert(_internalTransaction.State != null); if (_internalTransaction.State.IsCompleted(_internalTransaction)) { break; } } while (Monitor.Wait(_internalTransaction)); _internalTransaction.State.EndCommit(_internalTransaction); } if (etwLog.IsEnabled()) { etwLog.MethodExit(TraceSourceType.TraceSourceLtm, this); } } object? IAsyncResult.AsyncState => _internalTransaction._asyncState; bool IAsyncResult.CompletedSynchronously => false; WaitHandle IAsyncResult.AsyncWaitHandle { get { if (_internalTransaction._asyncResultEvent == null) { lock (_internalTransaction) { if (_internalTransaction._asyncResultEvent == null) { Debug.Assert(_internalTransaction.State != null); // Demand create an event that is already signaled if the transaction has completed. ManualResetEvent temp = new ManualResetEvent( _internalTransaction.State.get_Status(_internalTransaction) != TransactionStatus.Active); _internalTransaction._asyncResultEvent = temp; } } } return _internalTransaction._asyncResultEvent; } } bool IAsyncResult.IsCompleted { get { lock (_internalTransaction) { Debug.Assert(_internalTransaction.State != null); return _internalTransaction.State.get_Status(_internalTransaction) != TransactionStatus.Active; } } } } }