You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,193 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace System.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Transactions;
|
||||
|
||||
// AsyncResult starts acquired; Complete releases.
|
||||
[Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.ManualResetEvent, SupportsAsync = true, ReleaseMethod = "Complete")]
|
||||
abstract class TransactedAsyncResult : AsyncResult
|
||||
{
|
||||
IAsyncResult deferredTransactionalResult;
|
||||
TransactionSignalScope transactionContext;
|
||||
|
||||
protected TransactedAsyncResult(AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
SetBeforePrepareAsyncCompletionAction(BeforePrepareAsyncCompletion);
|
||||
SetCheckSyncValidationFunc(CheckSyncValidation);
|
||||
}
|
||||
|
||||
protected override bool OnContinueAsyncCompletion(IAsyncResult result)
|
||||
{
|
||||
if (this.transactionContext != null && !this.transactionContext.Signal(result))
|
||||
{
|
||||
// The TransactionScope isn't cleaned up yet and can't be done on this thread. Must defer
|
||||
// the callback (which is likely to attempt to commit the transaction) until later.
|
||||
return false;
|
||||
}
|
||||
|
||||
this.transactionContext = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckSyncValidation(IAsyncResult result)
|
||||
{
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
// Once we pass the check, we know that we own forward progress, so transactionContext is correct. Verify its state.
|
||||
if (this.transactionContext != null)
|
||||
{
|
||||
if (this.transactionContext.State != TransactionSignalState.Completed)
|
||||
{
|
||||
ThrowInvalidAsyncResult("Check/SyncContinue cannot be called from within the PrepareTransactionalCall using block.");
|
||||
}
|
||||
else if (this.transactionContext.IsSignalled)
|
||||
{
|
||||
// This is most likely to happen when result.CompletedSynchronously registers differently here and in the callback, which
|
||||
// is the fault of 'result'.
|
||||
ThrowInvalidAsyncResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (object.ReferenceEquals(result, this.deferredTransactionalResult))
|
||||
{
|
||||
// The transactionContext may not be current if forward progress has been made via the callback. Instead,
|
||||
// use deferredTransactionalResult to see if we are supposed to execute a post-transaction callback.
|
||||
//
|
||||
// Once we pass the check, we know that we own forward progress, so transactionContext is correct. Verify its state.
|
||||
if (this.transactionContext == null || !this.transactionContext.IsSignalled)
|
||||
{
|
||||
ThrowInvalidAsyncResult(result);
|
||||
}
|
||||
this.deferredTransactionalResult = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.transactionContext = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BeforePrepareAsyncCompletion()
|
||||
{
|
||||
if (this.transactionContext != null)
|
||||
{
|
||||
// It might be an old, leftover one, if an exception was thrown within the last using (PrepareTransactionalCall()) block.
|
||||
if (this.transactionContext.IsPotentiallyAbandoned)
|
||||
{
|
||||
this.transactionContext = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.transactionContext.Prepared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected IDisposable PrepareTransactionalCall(Transaction transaction)
|
||||
{
|
||||
if (this.transactionContext != null && !this.transactionContext.IsPotentiallyAbandoned)
|
||||
{
|
||||
ThrowInvalidAsyncResult("PrepareTransactionalCall should only be called as the object of non-nested using statements. If the Begin succeeds, Check/SyncContinue must be called before another PrepareTransactionalCall.");
|
||||
}
|
||||
|
||||
return this.transactionContext = transaction == null ? null : new TransactionSignalScope(this, transaction);
|
||||
}
|
||||
|
||||
enum TransactionSignalState
|
||||
{
|
||||
Ready = 0,
|
||||
Prepared,
|
||||
Completed,
|
||||
Abandoned,
|
||||
}
|
||||
|
||||
class TransactionSignalScope : SignalGate<IAsyncResult>, IDisposable
|
||||
{
|
||||
TransactionScope transactionScope;
|
||||
TransactedAsyncResult parent;
|
||||
|
||||
public TransactionSignalScope(TransactedAsyncResult result, Transaction transaction)
|
||||
{
|
||||
Fx.Assert(transaction != null, "Null Transaction provided to AsyncResult.TransactionSignalScope.");
|
||||
this.parent = result;
|
||||
this.transactionScope = TransactionHelper.CreateTransactionScope(transaction);
|
||||
}
|
||||
|
||||
public TransactionSignalState State { get; private set; }
|
||||
|
||||
public bool IsPotentiallyAbandoned
|
||||
{
|
||||
get
|
||||
{
|
||||
return State == TransactionSignalState.Abandoned || (State == TransactionSignalState.Completed && !IsSignalled);
|
||||
}
|
||||
}
|
||||
|
||||
public void Prepared()
|
||||
{
|
||||
if (State != TransactionSignalState.Ready)
|
||||
{
|
||||
AsyncResult.ThrowInvalidAsyncResult("PrepareAsyncCompletion should only be called once per PrepareTransactionalCall.");
|
||||
}
|
||||
State = TransactionSignalState.Prepared;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
if (State == TransactionSignalState.Ready)
|
||||
{
|
||||
State = TransactionSignalState.Abandoned;
|
||||
}
|
||||
else if (State == TransactionSignalState.Prepared)
|
||||
{
|
||||
State = TransactionSignalState.Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncResult.ThrowInvalidAsyncResult("PrepareTransactionalCall should only be called in a using. Dispose called multiple times.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
TransactionHelper.CompleteTransactionScope(ref this.transactionScope);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (Fx.IsFatal(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Complete and Dispose are not expected to throw. If they do it can mess up the AsyncResult state machine.
|
||||
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.AsyncTransactionException));
|
||||
}
|
||||
|
||||
// This will release the callback to run, or tell us that we need to defer the callback to Check/SyncContinue.
|
||||
//
|
||||
// It's possible to avoid this Interlocked when CompletedSynchronously is true, but we have no way of knowing that
|
||||
// from here, and adding a way would add complexity to the AsyncResult transactional calling pattern. This
|
||||
// unnecessary Interlocked only happens when: PrepareTransactionalCall is called with a non-null transaction,
|
||||
// PrepareAsyncCompletion is reached, and the operation completes synchronously or with an exception.
|
||||
IAsyncResult result;
|
||||
if (State == TransactionSignalState.Completed && Unlock(out result))
|
||||
{
|
||||
if (this.parent.deferredTransactionalResult != null)
|
||||
{
|
||||
AsyncResult.ThrowInvalidAsyncResult(this.parent.deferredTransactionalResult);
|
||||
}
|
||||
this.parent.deferredTransactionalResult = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user