Xamarin Public Jenkins (auto-signing) e79aa3c0ed Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2016-08-03 10:59:49 +00:00

1712 lines
70 KiB
C#

//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Runtime.DurableInstancing
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Security;
using System.Threading;
using System.Transactions;
using System.Xml.Linq;
using System.Runtime.Diagnostics;
[Fx.Tag.XamlVisible(false)]
public sealed class InstancePersistenceContext
{
readonly TimeSpan timeout;
Transaction transaction;
bool freezeTransaction;
CommittableTransaction myTransaction;
int cancellationHandlerCalled;
EventTraceActivity eventTraceActivity;
internal InstancePersistenceContext(InstanceHandle handle, Transaction transaction)
: this(handle)
{
Fx.Assert(transaction != null, "Null Transaction passed to InstancePersistenceContext.");
// Let's take our own clone of the transaction. We need to do this because we might need to
// create a TransactionScope using the transaction and in cases where we are dealing with a
// transaction that is flowed into the workflow on a message, the DependentTransaction that the
// dispatcher creates and sets to Transaction.Current may already be Completed by the time a
// Save operation is done. And since TransactionScope creates a DependentTransaction, it won't
// be able to.
// We don't create another DependentClone because we are going to do a EnlistVolatile on the
// transaction ourselves.
this.transaction = transaction.Clone();
IsHostTransaction = true;
this.eventTraceActivity = handle.EventTraceActivity;
}
internal InstancePersistenceContext(InstanceHandle handle, TimeSpan timeout)
: this(handle)
{
this.timeout = timeout;
}
InstancePersistenceContext(InstanceHandle handle)
{
Fx.Assert(handle != null, "Null handle passed to InstancePersistenceContext.");
InstanceHandle = handle;
// Fork a copy of the current view to be the new working view. It starts with no query results.
InstanceView newView = handle.View.Clone();
newView.InstanceStoreQueryResults = null;
InstanceView = newView;
this.cancellationHandlerCalled = 0;
}
public InstanceHandle InstanceHandle { get; private set; }
public InstanceView InstanceView { get; private set; }
public long InstanceVersion
{
get
{
return InstanceHandle.Version;
}
}
internal EventTraceActivity EventTraceActivity
{
get
{
return this.eventTraceActivity;
}
}
public Guid LockToken
{
get
{
Fx.Assert(InstanceHandle.Owner == null || InstanceHandle.Owner.OwnerToken == InstanceView.InstanceOwner.OwnerToken, "Mismatched lock tokens.");
// If the handle doesn't own the lock yet, return the owner LockToken, which is needed to check whether this owner already owns locks.
return InstanceHandle.Owner == null ? Guid.Empty : InstanceHandle.Owner.OwnerToken;
}
}
public object UserContext
{
get
{
return InstanceHandle.ProviderObject;
}
}
bool CancelRequested { get; set; }
ExecuteAsyncResult RootAsyncResult { get; set; }
ExecuteAsyncResult LastAsyncResult { get; set; }
bool IsHostTransaction { get; set; }
bool Active
{
get
{
return RootAsyncResult != null;
}
}
public void SetCancellationHandler(Action<InstancePersistenceContext> cancellationHandler)
{
ThrowIfNotActive("SetCancellationHandler");
LastAsyncResult.CancellationHandler = cancellationHandler;
if (CancelRequested && (cancellationHandler != null))
{
try
{
if (Interlocked.CompareExchange(ref this.cancellationHandlerCalled, 0, 1) == 0)
{
cancellationHandler(this);
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
throw Fx.Exception.AsError(new CallbackException(SRCore.OnCancelRequestedThrew, exception));
}
}
}
public void BindInstanceOwner(Guid instanceOwnerId, Guid lockToken)
{
if (instanceOwnerId == Guid.Empty)
{
throw Fx.Exception.Argument("instanceOwnerId", SRCore.GuidCannotBeEmpty);
}
if (lockToken == Guid.Empty)
{
throw Fx.Exception.Argument("lockToken", SRCore.GuidCannotBeEmpty);
}
ThrowIfNotActive("BindInstanceOwner");
InstanceOwner owner = InstanceHandle.Store.GetOrCreateOwner(instanceOwnerId, lockToken);
InstanceView.BindOwner(owner);
IsHandleDoomedByRollback = true;
InstanceHandle.BindOwner(owner);
}
public void BindInstance(Guid instanceId)
{
if (instanceId == Guid.Empty)
{
throw Fx.Exception.Argument("instanceId", SRCore.GuidCannotBeEmpty);
}
ThrowIfNotActive("BindInstance");
InstanceView.BindInstance(instanceId);
IsHandleDoomedByRollback = true;
InstanceHandle.BindInstance(instanceId);
}
public void BindEvent(InstancePersistenceEvent persistenceEvent)
{
if (persistenceEvent == null)
{
throw Fx.Exception.ArgumentNull("persistenceEvent");
}
ThrowIfNotActive("BindEvent");
if (!InstanceView.IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToOwner));
}
IsHandleDoomedByRollback = true;
InstanceHandle.BindOwnerEvent(persistenceEvent);
}
public void BindAcquiredLock(long instanceVersion)
{
if (instanceVersion < 0)
{
throw Fx.Exception.ArgumentOutOfRange("instanceVersion", instanceVersion, SRCore.InvalidLockToken);
}
ThrowIfNotActive("BindAcquiredLock");
// This call has a synchronization, so we are guaranteed it is only successful once.
InstanceView.BindLock(instanceVersion);
IsHandleDoomedByRollback = true;
InstanceHandle.Bind(instanceVersion);
}
public void BindReclaimedLock(long instanceVersion, TimeSpan timeout)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("BindReclaimedLock", instanceVersion, timeout);
if (!wait.Wait(timeout))
{
InstanceHandle.CancelReclaim(new TimeoutException(SRCore.TimedOutWaitingForLockResolution));
}
ConcludeBindReclaimedLockHelper();
}
public IAsyncResult BeginBindReclaimedLock(long instanceVersion, TimeSpan timeout, AsyncCallback callback, object state)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("BeginBindReclaimedLock", instanceVersion, timeout);
return new BindReclaimedLockAsyncResult(this, wait, timeout, callback, state);
}
public void EndBindReclaimedLock(IAsyncResult result)
{
BindReclaimedLockAsyncResult.End(result);
}
public Exception CreateBindReclaimedLockException(long instanceVersion)
{
AsyncWaitHandle wait = InitiateBindReclaimedLockHelper("CreateBindReclaimedLockException", instanceVersion, TimeSpan.MaxValue);
return new BindReclaimedLockException(wait);
}
AsyncWaitHandle InitiateBindReclaimedLockHelper(string methodName, long instanceVersion, TimeSpan timeout)
{
if (instanceVersion < 0)
{
throw Fx.Exception.ArgumentOutOfRange("instanceVersion", instanceVersion, SRCore.InvalidLockToken);
}
TimeoutHelper.ThrowIfNegativeArgument(timeout);
ThrowIfNotActive(methodName);
// This call has a synchronization, so we are guaranteed it is only successful once.
InstanceView.StartBindLock(instanceVersion);
IsHandleDoomedByRollback = true;
AsyncWaitHandle wait = InstanceHandle.StartReclaim(instanceVersion);
if (wait == null)
{
InstanceHandle.Free();
throw Fx.Exception.AsError(new InstanceHandleConflictException(LastAsyncResult.CurrentCommand.Name, InstanceView.InstanceId));
}
return wait;
}
void ConcludeBindReclaimedLockHelper()
{
// If FinishReclaim doesn't throw an exception, we are done - the reclaim was successful.
// The Try / Finally makes up for the reverse order of setting the handle, then the view.
long instanceVersion = -1;
try
{
if (!InstanceHandle.FinishReclaim(ref instanceVersion))
{
InstanceHandle.Free();
throw Fx.Exception.AsError(new InstanceHandleConflictException(LastAsyncResult.CurrentCommand.Name, InstanceView.InstanceId));
}
Fx.Assert(instanceVersion >= 0, "Where did the instance version go?");
}
finally
{
if (instanceVersion >= 0)
{
InstanceView.FinishBindLock(instanceVersion);
}
}
}
public void PersistedInstance(IDictionary<XName, InstanceValue> data)
{
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("PersistedInstance");
InstanceView.InstanceData = data.ReadOnlyCopy(true);
InstanceView.InstanceDataConsistency = InstanceValueConsistency.None;
InstanceView.InstanceState = InstanceState.Initialized;
}
public void LoadedInstance(InstanceState state, IDictionary<XName, InstanceValue> instanceData, IDictionary<XName, InstanceValue> instanceMetadata, IDictionary<Guid, IDictionary<XName, InstanceValue>> associatedInstanceKeyMetadata, IDictionary<Guid, IDictionary<XName, InstanceValue>> completedInstanceKeyMetadata)
{
if (state == InstanceState.Uninitialized)
{
if (instanceData != null && instanceData.Count > 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.UninitializedCannotHaveData));
}
}
else if (state == InstanceState.Completed)
{
if (associatedInstanceKeyMetadata != null && associatedInstanceKeyMetadata.Count > 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CompletedMustNotHaveAssociatedKeys));
}
}
else if (state != InstanceState.Initialized)
{
throw Fx.Exception.Argument("state", SRCore.InvalidInstanceState);
}
ThrowIfNoInstance();
ThrowIfNotActive("PersistedInstance");
InstanceValueConsistency consistency = InstanceView.IsBoundToLock || state == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
ReadOnlyDictionaryInternal<XName, InstanceValue> instanceDataCopy = instanceData.ReadOnlyCopy(false);
ReadOnlyDictionaryInternal<XName, InstanceValue> instanceMetadataCopy = instanceMetadata.ReadOnlyCopy(false);
Dictionary<Guid, InstanceKeyView> keysCopy = null;
int totalKeys = (associatedInstanceKeyMetadata != null ? associatedInstanceKeyMetadata.Count : 0) + (completedInstanceKeyMetadata != null ? completedInstanceKeyMetadata.Count : 0);
if (totalKeys > 0)
{
keysCopy = new Dictionary<Guid, InstanceKeyView>(totalKeys);
}
if (associatedInstanceKeyMetadata != null && associatedInstanceKeyMetadata.Count > 0)
{
foreach (KeyValuePair<Guid, IDictionary<XName, InstanceValue>> keyMetadata in associatedInstanceKeyMetadata)
{
InstanceKeyView view = new InstanceKeyView(keyMetadata.Key);
view.InstanceKeyState = InstanceKeyState.Associated;
view.InstanceKeyMetadata = keyMetadata.Value.ReadOnlyCopy(false);
view.InstanceKeyMetadataConsistency = InstanceView.IsBoundToLock ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
keysCopy.Add(view.InstanceKey, view);
}
}
if (completedInstanceKeyMetadata != null && completedInstanceKeyMetadata.Count > 0)
{
foreach (KeyValuePair<Guid, IDictionary<XName, InstanceValue>> keyMetadata in completedInstanceKeyMetadata)
{
InstanceKeyView view = new InstanceKeyView(keyMetadata.Key);
view.InstanceKeyState = InstanceKeyState.Completed;
view.InstanceKeyMetadata = keyMetadata.Value.ReadOnlyCopy(false);
view.InstanceKeyMetadataConsistency = consistency;
keysCopy.Add(view.InstanceKey, view);
}
}
InstanceView.InstanceState = state;
InstanceView.InstanceData = instanceDataCopy;
InstanceView.InstanceDataConsistency = consistency;
InstanceView.InstanceMetadata = instanceMetadataCopy;
InstanceView.InstanceMetadataConsistency = consistency;
InstanceView.InstanceKeys = keysCopy == null ? null : new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(keysCopy);
InstanceView.InstanceKeysConsistency = consistency;
}
public void CompletedInstance()
{
ThrowIfNotLocked();
ThrowIfUninitialized();
ThrowIfCompleted();
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
foreach (KeyValuePair<Guid, InstanceKeyView> key in InstanceView.InstanceKeys)
{
if (key.Value.InstanceKeyState == InstanceKeyState.Associated)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotCompleteWithKeys));
}
}
}
ThrowIfNotTransactional("CompletedInstance");
InstanceView.InstanceState = InstanceState.Completed;
}
public void ReadInstanceMetadata(IDictionary<XName, InstanceValue> metadata, bool complete)
{
ThrowIfNoInstance();
ThrowIfNotActive("ReadInstanceMetadata");
if (InstanceView.InstanceMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
InstanceView.InstanceMetadata = metadata.ReadOnlyCopy(false);
InstanceView.InstanceMetadataConsistency = InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
}
else
{
if ((InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed) && (InstanceView.InstanceMetadataConsistency & InstanceValueConsistency.InDoubt) != 0)
{
// In this case, prefer throwing out old data and keeping only authoritative data.
InstanceView.InstanceMetadata = metadata.ReadOnlyMergeInto(null, false);
InstanceView.InstanceMetadataConsistency = InstanceValueConsistency.Partial;
}
else
{
InstanceView.InstanceMetadata = metadata.ReadOnlyMergeInto(InstanceView.InstanceMetadata, false);
InstanceView.InstanceMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
}
public void WroteInstanceMetadataValue(XName name, InstanceValue value)
{
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("WroteInstanceMetadataValue");
InstanceView.AccumulatedMetadataWrites[name] = value;
}
public void AssociatedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("AssociatedInstanceKey");
Dictionary<Guid, InstanceKeyView> copy = new Dictionary<Guid, InstanceKeyView>(InstanceView.InstanceKeys);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0 && copy.ContainsKey(key))
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyAssociated));
}
InstanceKeyView keyView = new InstanceKeyView(key);
keyView.InstanceKeyState = InstanceKeyState.Associated;
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.None;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(copy);
}
public void CompletedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("CompletedInstanceKey");
InstanceKeyView existingKeyView;
InstanceView.InstanceKeys.TryGetValue(key, out existingKeyView);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
if (existingKeyView != null)
{
if (existingKeyView.InstanceKeyState == InstanceKeyState.Completed)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyCompleted));
}
}
else if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.Partial) == 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
}
if (existingKeyView != null)
{
existingKeyView.InstanceKeyState = InstanceKeyState.Completed;
}
else
{
Dictionary<Guid, InstanceKeyView> copy = new Dictionary<Guid, InstanceKeyView>(InstanceView.InstanceKeys);
InstanceKeyView keyView = new InstanceKeyView(key);
keyView.InstanceKeyState = InstanceKeyState.Completed;
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(copy);
}
}
public void UnassociatedInstanceKey(Guid key)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("UnassociatedInstanceKey");
InstanceKeyView existingKeyView;
InstanceView.InstanceKeys.TryGetValue(key, out existingKeyView);
if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.InDoubt) == 0)
{
if (existingKeyView != null)
{
if (existingKeyView.InstanceKeyState == InstanceKeyState.Associated)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotCompleted));
}
}
else if ((InstanceView.InstanceKeysConsistency & InstanceValueConsistency.Partial) == 0)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyAlreadyUnassociated));
}
}
if (existingKeyView != null)
{
Dictionary<Guid, InstanceKeyView> copy = new Dictionary<Guid, InstanceKeyView>(InstanceView.InstanceKeys);
copy.Remove(key);
InstanceView.InstanceKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(copy);
}
}
public void ReadInstanceKeyMetadata(Guid key, IDictionary<XName, InstanceValue> metadata, bool complete)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
ThrowIfNoInstance();
ThrowIfNotActive("ReadInstanceKeyMetadata");
InstanceKeyView keyView;
if (!InstanceView.InstanceKeys.TryGetValue(key, out keyView))
{
if (InstanceView.InstanceKeysConsistency == InstanceValueConsistency.None)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
Dictionary<Guid, InstanceKeyView> copy = new Dictionary<Guid, InstanceKeyView>(InstanceView.InstanceKeys);
keyView = new InstanceKeyView(key);
if (complete)
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyCopy(false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.None;
}
else
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(null, false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
}
if (!InstanceView.IsBoundToLock && InstanceView.InstanceState != InstanceState.Completed)
{
keyView.InstanceKeyMetadataConsistency |= InstanceValueConsistency.InDoubt;
}
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(copy);
}
else
{
if (keyView.InstanceKeyMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyCopy(false);
keyView.InstanceKeyMetadataConsistency = InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed ? InstanceValueConsistency.None : InstanceValueConsistency.InDoubt;
}
else
{
if ((InstanceView.IsBoundToLock || InstanceView.InstanceState == InstanceState.Completed) && (keyView.InstanceKeyMetadataConsistency & InstanceValueConsistency.InDoubt) != 0)
{
// In this case, prefer throwing out old data and keeping only authoritative data.
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(null, false);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
}
else
{
keyView.InstanceKeyMetadata = metadata.ReadOnlyMergeInto(keyView.InstanceKeyMetadata, false);
keyView.InstanceKeyMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
}
}
public void WroteInstanceKeyMetadataValue(Guid key, XName name, InstanceValue value)
{
if (key == Guid.Empty)
{
throw Fx.Exception.Argument("key", SRCore.InvalidKeyArgument);
}
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNotLocked();
ThrowIfCompleted();
ThrowIfNotTransactional("WroteInstanceKeyMetadataValue");
InstanceKeyView keyView;
if (!InstanceView.InstanceKeys.TryGetValue(key, out keyView))
{
if (InstanceView.InstanceKeysConsistency == InstanceValueConsistency.None)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.KeyNotAssociated));
}
if (!value.IsWriteOnly() && !value.IsDeletedValue)
{
Dictionary<Guid, InstanceKeyView> copy = new Dictionary<Guid, InstanceKeyView>(InstanceView.InstanceKeys);
keyView = new InstanceKeyView(key);
keyView.AccumulatedMetadataWrites.Add(name, value);
keyView.InstanceKeyMetadataConsistency = InstanceValueConsistency.Partial;
copy[keyView.InstanceKey] = keyView;
InstanceView.InstanceKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(copy);
InstanceView.InstanceKeysConsistency |= InstanceValueConsistency.Partial;
}
}
else
{
keyView.AccumulatedMetadataWrites.Add(name, value);
}
}
public void ReadInstanceOwnerMetadata(IDictionary<XName, InstanceValue> metadata, bool complete)
{
ThrowIfNoOwner();
ThrowIfNotActive("ReadInstanceOwnerMetadata");
if (InstanceView.InstanceOwnerMetadataConsistency == InstanceValueConsistency.None)
{
return;
}
if (complete)
{
InstanceView.InstanceOwnerMetadata = metadata.ReadOnlyCopy(false);
InstanceView.InstanceOwnerMetadataConsistency = InstanceValueConsistency.InDoubt;
}
else
{
InstanceView.InstanceOwnerMetadata = metadata.ReadOnlyMergeInto(InstanceView.InstanceOwnerMetadata, false);
InstanceView.InstanceOwnerMetadataConsistency |= InstanceValueConsistency.Partial;
}
}
public void WroteInstanceOwnerMetadataValue(XName name, InstanceValue value)
{
if (name == null)
{
throw Fx.Exception.ArgumentNull("name");
}
if (value == null)
{
throw Fx.Exception.ArgumentNull("value");
}
ThrowIfNoOwner();
ThrowIfNotTransactional("WroteInstanceOwnerMetadataValue");
InstanceView.AccumulatedOwnerMetadataWrites.Add(name, value);
}
public void QueriedInstanceStore(InstanceStoreQueryResult queryResult)
{
if (queryResult == null)
{
throw Fx.Exception.ArgumentNull("queryResult");
}
ThrowIfNotActive("QueriedInstanceStore");
InstanceView.QueryResultsBacking.Add(queryResult);
}
[Fx.Tag.Throws.Timeout("The operation timed out.")]
[Fx.Tag.Throws(typeof(OperationCanceledException), "The operation was canceled because the InstanceHandle has been freed.")]
[Fx.Tag.Throws(typeof(InstancePersistenceException), "A command failed.")]
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree")]
public void Execute(InstancePersistenceCommand command, TimeSpan timeout)
{
if (command == null)
{
throw Fx.Exception.ArgumentNull("command");
}
ThrowIfNotActive("Execute");
try
{
ReconcileTransaction();
ExecuteAsyncResult.End(new ExecuteAsyncResult(this, command, timeout));
}
catch (TimeoutException)
{
InstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
InstanceHandle.Free();
throw;
}
}
// For each level of hierarchy of command execution, only one BeginExecute may be pending at a time.
[Fx.Tag.InheritThrows(From = "Execute")]
public IAsyncResult BeginExecute(InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
{
if (command == null)
{
throw Fx.Exception.ArgumentNull("command");
}
ThrowIfNotActive("BeginExecute");
try
{
ReconcileTransaction();
return new ExecuteAsyncResult(this, command, timeout, callback, state);
}
catch (TimeoutException)
{
InstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
InstanceHandle.Free();
throw;
}
}
[Fx.Tag.InheritThrows(From = "Execute")]
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", Conditional = "!result.IsCompleted")]
public void EndExecute(IAsyncResult result)
{
ExecuteAsyncResult.End(result);
}
internal Transaction Transaction
{
get
{
return this.transaction;
}
}
internal bool IsHandleDoomedByRollback { get; private set; }
internal void RequireTransaction()
{
if (this.transaction != null)
{
return;
}
Fx.AssertAndThrow(!this.freezeTransaction, "RequireTransaction called when transaction is frozen.");
Fx.AssertAndThrow(Active, "RequireTransaction called when no command is active.");
// It's ok if some time has passed since the timeout value was acquired, it is ok to run long. This transaction is not generally responsible
// for timing out the Execute operation. The exception to this rule is during Commit.
this.myTransaction = new CommittableTransaction(new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = this.timeout });
Transaction clone = this.myTransaction.Clone();
RootAsyncResult.SetInteriorTransaction(this.myTransaction, true);
this.transaction = clone;
}
internal void PrepareForReuse()
{
Fx.AssertAndThrow(!Active, "Prior use not yet complete!");
Fx.AssertAndThrow(IsHostTransaction, "Can only reuse contexts with host transactions.");
}
internal void NotifyHandleFree()
{
CancelRequested = true;
ExecuteAsyncResult lastAsyncResult = LastAsyncResult;
Action<InstancePersistenceContext> onCancel = lastAsyncResult == null ? null : lastAsyncResult.CancellationHandler;
if (onCancel != null)
{
try
{
if (Interlocked.CompareExchange(ref this.cancellationHandlerCalled, 0, 1) == 0)
{
onCancel(this);
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
throw Fx.Exception.AsError(new CallbackException(SRCore.OnCancelRequestedThrew, exception));
}
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree")]
internal static InstanceView OuterExecute(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout)
{
try
{
return ExecuteAsyncResult.End(new ExecuteAsyncResult(initialInstanceHandle, command, transaction, timeout));
}
catch (TimeoutException)
{
initialInstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
initialInstanceHandle.Free();
throw;
}
}
internal static IAsyncResult BeginOuterExecute(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
{
try
{
return new ExecuteAsyncResult(initialInstanceHandle, command, transaction, timeout, callback, state);
}
catch (TimeoutException)
{
initialInstanceHandle.Free();
throw;
}
catch (OperationCanceledException)
{
initialInstanceHandle.Free();
throw;
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", Conditional = "!result.IsCompleted")]
internal static InstanceView EndOuterExecute(IAsyncResult result)
{
InstanceView finalState = ExecuteAsyncResult.End(result);
if (finalState == null)
{
throw Fx.Exception.Argument("result", InternalSR.InvalidAsyncResult);
}
return finalState;
}
void ThrowIfNotLocked()
{
if (!InstanceView.IsBoundToLock)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresLock));
}
}
void ThrowIfNoInstance()
{
if (!InstanceView.IsBoundToInstance)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresInstance));
}
}
void ThrowIfNoOwner()
{
if (!InstanceView.IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresOwner));
}
}
void ThrowIfCompleted()
{
if (InstanceView.IsBoundToLock && InstanceView.InstanceState == InstanceState.Completed)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresNotCompleted));
}
}
void ThrowIfUninitialized()
{
if (InstanceView.IsBoundToLock && InstanceView.InstanceState == InstanceState.Uninitialized)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceOperationRequiresNotUninitialized));
}
}
void ThrowIfNotActive(string methodName)
{
if (!Active)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.OutsideInstanceExecutionScope(methodName)));
}
}
void ThrowIfNotTransactional(string methodName)
{
ThrowIfNotActive(methodName);
if (RootAsyncResult.CurrentCommand.IsTransactionEnlistmentOptional)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.OutsideTransactionalCommand(methodName)));
}
}
void ReconcileTransaction()
{
// If the provider fails to flow the transaction, that's fine, we don't consider that a request
// not to use one.
Transaction transaction = Transaction.Current;
if (transaction != null)
{
if (this.transaction == null)
{
if (this.freezeTransaction)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.MustSetTransactionOnFirstCall));
}
RootAsyncResult.SetInteriorTransaction(transaction, false);
this.transaction = transaction;
}
else if (!transaction.Equals(this.transaction))
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotReplaceTransaction));
}
}
this.freezeTransaction = true;
}
class ExecuteAsyncResult : TransactedAsyncResult, ISinglePhaseNotification
{
static AsyncCompletion onAcquireContext = new AsyncCompletion(OnAcquireContext);
static AsyncCompletion onTryCommand = new AsyncCompletion(OnTryCommand);
static AsyncCompletion onCommit = new AsyncCompletion(OnCommit);
static Action<object, TimeoutException> onBindReclaimed = new Action<object, TimeoutException>(OnBindReclaimed);
static Action<object, TimeoutException> onCommitWait = new Action<object, TimeoutException>(OnCommitWait);
readonly InstanceHandle initialInstanceHandle;
readonly Stack<IEnumerator<InstancePersistenceCommand>> executionStack;
readonly TimeoutHelper timeoutHelper;
readonly ExecuteAsyncResult priorAsyncResult;
InstancePersistenceContext context;
CommittableTransaction transactionToCommit;
IEnumerator<InstancePersistenceCommand> currentExecution;
AsyncWaitHandle waitForTransaction;
Action<InstancePersistenceContext> cancellationHandler;
bool executeCalledByCurrentCommand;
bool rolledBack;
bool inDoubt;
InstanceView finalState;
public ExecuteAsyncResult(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
: this(command, timeout, callback, state)
{
this.initialInstanceHandle = initialInstanceHandle;
OnCompleting = new Action<AsyncResult, Exception>(SimpleCleanup);
IAsyncResult result = this.initialInstanceHandle.BeginAcquireExecutionContext(transaction, this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(ExecuteAsyncResult.onAcquireContext), this);
if (result.CompletedSynchronously)
{
// After this stage, must complete explicitly in order to get Cleanup to run correctly.
bool completeSelf = false;
Exception completionException = null;
try
{
completeSelf = OnAcquireContext(result);
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
completeSelf = true;
completionException = exception;
}
if (completeSelf)
{
Complete(true, completionException);
}
}
}
public ExecuteAsyncResult(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
: this(command, timeout, callback, state)
{
this.context = context;
this.priorAsyncResult = this.context.LastAsyncResult;
Fx.Assert(this.priorAsyncResult != null, "The LastAsyncResult should already have been checked.");
this.priorAsyncResult.executeCalledByCurrentCommand = true;
OnCompleting = new Action<AsyncResult, Exception>(SimpleCleanup);
bool completeSelf = false;
bool success = false;
try
{
this.context.LastAsyncResult = this;
if (RunLoop())
{
completeSelf = true;
}
success = true;
}
finally
{
if (!success)
{
this.context.LastAsyncResult = this.priorAsyncResult;
}
}
if (completeSelf)
{
Complete(true);
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", CancelDeclaringType = typeof(InstancePersistenceContext))]
public ExecuteAsyncResult(InstanceHandle initialInstanceHandle, InstancePersistenceCommand command, Transaction transaction, TimeSpan timeout)
: this(command, timeout, null, null)
{
this.initialInstanceHandle = initialInstanceHandle;
this.context = this.initialInstanceHandle.AcquireExecutionContext(transaction, this.timeoutHelper.RemainingTime());
Exception completionException = null;
try
{
// After this stage, must complete explicitly in order to get Cleanup to run correctly.
this.context.RootAsyncResult = this;
this.context.LastAsyncResult = this;
OnCompleting = new Action<AsyncResult, Exception>(Cleanup);
RunLoopCore(true);
if (this.transactionToCommit != null)
{
try
{
this.transactionToCommit.Commit();
}
catch (TransactionException)
{
// Since we are enlisted in this transaction, we can ignore exceptions from Commit.
}
this.transactionToCommit = null;
}
DoWaitForTransaction(true);
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
completionException = exception;
}
Complete(true, completionException);
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", CancelDeclaringType = typeof(InstancePersistenceContext))]
public ExecuteAsyncResult(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout)
: this(command, timeout, null, null)
{
this.context = context;
this.priorAsyncResult = this.context.LastAsyncResult;
Fx.Assert(this.priorAsyncResult != null, "The LastAsyncResult should already have been checked.");
this.priorAsyncResult.executeCalledByCurrentCommand = true;
bool success = false;
try
{
this.context.LastAsyncResult = this;
RunLoopCore(true);
success = true;
}
finally
{
this.context.LastAsyncResult = this.priorAsyncResult;
if (!success && this.context.IsHandleDoomedByRollback)
{
this.context.InstanceHandle.Free();
}
}
Complete(true);
}
ExecuteAsyncResult(InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
: base(callback, state)
{
this.executionStack = new Stack<IEnumerator<InstancePersistenceCommand>>(2);
this.timeoutHelper = new TimeoutHelper(timeout);
this.currentExecution = (new List<InstancePersistenceCommand> { command }).GetEnumerator();
}
internal InstancePersistenceCommand CurrentCommand { get; private set; }
internal Action<InstancePersistenceContext> CancellationHandler
{
get
{
Action<InstancePersistenceContext> handler = this.cancellationHandler;
ExecuteAsyncResult current = this;
while (handler == null)
{
current = current.priorAsyncResult;
if (current == null)
{
break;
}
handler = current.cancellationHandler;
}
return handler;
}
set
{
this.cancellationHandler = value;
}
}
public void SetInteriorTransaction(Transaction interiorTransaction, bool needsCommit)
{
Fx.Assert(!this.context.IsHostTransaction, "SetInteriorTransaction called for a host transaction.");
if (this.waitForTransaction != null)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ExecuteMustBeNested));
}
bool success = false;
try
{
this.waitForTransaction = new AsyncWaitHandle(EventResetMode.ManualReset);
interiorTransaction.EnlistVolatile(this, EnlistmentOptions.None);
success = true;
}
finally
{
if (!success)
{
if (this.waitForTransaction != null)
{
this.waitForTransaction.Set();
}
}
else if (needsCommit)
{
this.transactionToCommit = (CommittableTransaction)interiorTransaction;
}
}
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", CancelDeclaringType = typeof(InstancePersistenceContext), Conditional = "!result.IsCOmpleted")]
public static InstanceView End(IAsyncResult result)
{
ExecuteAsyncResult thisPtr = AsyncResult.End<ExecuteAsyncResult>(result);
Fx.Assert((thisPtr.finalState == null) == (thisPtr.initialInstanceHandle == null), "Should have thrown an exception if this is null on the outer result.");
return thisPtr.finalState;
}
static bool OnAcquireContext(IAsyncResult result)
{
ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)result.AsyncState;
thisPtr.context = thisPtr.initialInstanceHandle.EndAcquireExecutionContext(result);
thisPtr.context.RootAsyncResult = thisPtr;
thisPtr.context.LastAsyncResult = thisPtr;
thisPtr.OnCompleting = new Action<AsyncResult, Exception>(thisPtr.Cleanup);
return thisPtr.RunLoop();
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", CancelDeclaringType = typeof(InstancePersistenceContext), Conditional = "synchronous")]
bool RunLoopCore(bool synchronous)
{
while (this.currentExecution != null)
{
if (this.currentExecution.MoveNext())
{
bool isFirstCommand = CurrentCommand == null;
this.executeCalledByCurrentCommand = false;
CurrentCommand = this.currentExecution.Current;
Fx.Assert(isFirstCommand || this.executionStack.Count > 0, "The first command should always remain at the top of the stack.");
if (isFirstCommand)
{
if (this.priorAsyncResult != null)
{
if (this.priorAsyncResult.CurrentCommand.IsTransactionEnlistmentOptional && !CurrentCommand.IsTransactionEnlistmentOptional)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotInvokeTransactionalFromNonTransactional));
}
}
}
else if (this.executionStack.Peek().Current.IsTransactionEnlistmentOptional)
{
if (!CurrentCommand.IsTransactionEnlistmentOptional)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotInvokeTransactionalFromNonTransactional));
}
}
else if (this.priorAsyncResult == null)
{
// This is not the first command. Since the whole thing wasn't done at once by the
// provider, force a transaction if the first command required one.
this.context.RequireTransaction();
}
// Intentionally calling MayBindLockToInstanceHandle prior to Validate. This is a publically visible order.
bool mayBindLockToInstanceHandle = CurrentCommand.AutomaticallyAcquiringLock;
CurrentCommand.Validate(this.context.InstanceView);
if (mayBindLockToInstanceHandle)
{
if (isFirstCommand)
{
if (this.priorAsyncResult != null)
{
if (!this.priorAsyncResult.CurrentCommand.AutomaticallyAcquiringLock)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotInvokeBindingFromNonBinding));
}
}
else if (!this.context.InstanceView.IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.MayBindLockCommandShouldValidateOwner));
}
else if (!this.context.InstanceView.IsBoundToLock)
{
// This is the first command in the set and it may lock, so we must start the bind.
this.context.InstanceHandle.StartPotentialBind();
}
}
else if (!this.executionStack.Peek().Current.AutomaticallyAcquiringLock)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CannotInvokeBindingFromNonBinding));
}
}
if (this.context.CancelRequested)
{
throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed));
}
BindReclaimedLockException bindReclaimedLockException = null;
if (synchronous)
{
bool commandProcessed;
TransactionScope txScope = null;
try
{
txScope = TransactionHelper.CreateTransactionScope(this.context.Transaction);
commandProcessed = this.context.InstanceHandle.Store.TryCommand(this.context, CurrentCommand, this.timeoutHelper.RemainingTime());
}
catch (BindReclaimedLockException exception)
{
bindReclaimedLockException = exception;
commandProcessed = true;
}
finally
{
TransactionHelper.CompleteTransactionScope(ref txScope);
}
AfterCommand(commandProcessed);
if (bindReclaimedLockException != null)
{
BindReclaimed(!bindReclaimedLockException.MarkerWaitHandle.Wait(this.timeoutHelper.RemainingTime()));
}
}
else
{
IAsyncResult result;
using (PrepareTransactionalCall(this.context.Transaction))
{
try
{
result = this.context.InstanceHandle.Store.BeginTryCommand(this.context, CurrentCommand, this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(ExecuteAsyncResult.onTryCommand), this);
}
catch (BindReclaimedLockException exception)
{
bindReclaimedLockException = exception;
result = null;
}
}
if (result == null)
{
AfterCommand(true);
if (!bindReclaimedLockException.MarkerWaitHandle.WaitAsync(ExecuteAsyncResult.onBindReclaimed, this, this.timeoutHelper.RemainingTime()))
{
return false;
}
BindReclaimed(false);
}
else
{
if (!CheckSyncContinue(result) || !DoEndCommand(result))
{
return false;
}
}
}
}
else if (this.executionStack.Count > 0)
{
this.currentExecution = this.executionStack.Pop();
}
else
{
this.currentExecution = null;
}
}
CurrentCommand = null;
return true;
}
bool RunLoop()
{
if (!RunLoopCore(false))
{
return false;
}
// If this is an inner command, return true right away to continue this execution episode in a different async result.
if (this.initialInstanceHandle == null)
{
return true;
}
// This is is an outer scope. We need to commit and/or wait for commit if necessary.
if (this.transactionToCommit != null)
{
IAsyncResult result = null;
try
{
result = this.transactionToCommit.BeginCommit(PrepareAsyncCompletion(ExecuteAsyncResult.onCommit), this);
}
catch (TransactionException)
{
// Since we are enlisted in the transaction, we can ignore exceptions from Commit.
this.transactionToCommit = null;
}
if (result != null)
{
return result.CompletedSynchronously ? OnCommit(result) : false;
}
}
return DoWaitForTransaction(false);
}
static bool OnTryCommand(IAsyncResult result)
{
ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)result.AsyncState;
return thisPtr.DoEndCommand(result) && thisPtr.RunLoop();
}
[Fx.Tag.GuaranteeNonBlocking]
bool DoEndCommand(IAsyncResult result)
{
bool commandProcessed;
BindReclaimedLockException bindReclaimedLockException = null;
try
{
commandProcessed = this.context.InstanceHandle.Store.EndTryCommand(result);
}
catch (BindReclaimedLockException exception)
{
bindReclaimedLockException = exception;
commandProcessed = true;
}
AfterCommand(commandProcessed);
if (bindReclaimedLockException != null)
{
if (!bindReclaimedLockException.MarkerWaitHandle.WaitAsync(ExecuteAsyncResult.onBindReclaimed, this, this.timeoutHelper.RemainingTime()))
{
return false;
}
BindReclaimed(false);
}
return true;
}
void AfterCommand(bool commandProcessed)
{
if (!object.ReferenceEquals(this.context.LastAsyncResult, this))
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ExecuteMustBeNested));
}
if (!commandProcessed)
{
if (this.executeCalledByCurrentCommand)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.TryCommandCannotExecuteSubCommandsAndReduce));
}
IEnumerable<InstancePersistenceCommand> reduction = CurrentCommand.Reduce(this.context.InstanceView);
if (reduction == null)
{
throw Fx.Exception.AsError(new NotSupportedException(SRCore.ProviderDoesNotSupportCommand(CurrentCommand.Name)));
}
this.executionStack.Push(this.currentExecution);
this.currentExecution = reduction.GetEnumerator();
}
}
static void OnBindReclaimed(object state, TimeoutException timeoutException)
{
ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)state;
bool completeSelf;
Exception completionException = null;
try
{
thisPtr.BindReclaimed(timeoutException != null);
completeSelf = thisPtr.RunLoop();
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
completionException = exception;
completeSelf = true;
}
if (completeSelf)
{
thisPtr.Complete(false, completionException);
}
}
void BindReclaimed(bool timedOut)
{
if (timedOut)
{
this.context.InstanceHandle.CancelReclaim(new TimeoutException(SRCore.TimedOutWaitingForLockResolution));
}
this.context.ConcludeBindReclaimedLockHelper();
// If we get here, the reclaim attempt succeeded and we own the lock - but we are in the
// CreateBindReclaimedLockException path, which auto-cancels on success.
this.context.InstanceHandle.Free();
throw Fx.Exception.AsError(new OperationCanceledException(SRCore.BindReclaimSucceeded));
}
[Fx.Tag.GuaranteeNonBlocking]
static bool OnCommit(IAsyncResult result)
{
ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)result.AsyncState;
try
{
thisPtr.transactionToCommit.EndCommit(result);
}
catch (TransactionException)
{
// Since we are enlisted in the transaction, we can ignore exceptions from Commit.
}
thisPtr.transactionToCommit = null;
return thisPtr.DoWaitForTransaction(false);
}
[Fx.Tag.Blocking(CancelMethod = "NotifyHandleFree", CancelDeclaringType = typeof(InstancePersistenceContext), Conditional = "synchronous")]
bool DoWaitForTransaction(bool synchronous)
{
if (this.waitForTransaction != null)
{
if (synchronous)
{
TimeSpan waitTimeout = this.timeoutHelper.RemainingTime();
if (!this.waitForTransaction.Wait(waitTimeout))
{
throw Fx.Exception.AsError(new TimeoutException(InternalSR.TimeoutOnOperation(waitTimeout)));
}
}
else
{
if (!this.waitForTransaction.WaitAsync(ExecuteAsyncResult.onCommitWait, this, this.timeoutHelper.RemainingTime()))
{
return false;
}
}
Exception exception = AfterCommitWait();
if (exception != null)
{
throw Fx.Exception.AsError(exception);
}
}
else if (this.context.IsHostTransaction)
{
// For host transactions, we need to provide a clone of the intermediate state as the final state.
this.finalState = this.context.InstanceView.Clone();
this.finalState.MakeReadOnly();
// The intermediate state should have the query results cleared - they are per-call of Execute.
this.context.InstanceView.InstanceStoreQueryResults = null;
}
else
{
// If we get here, there's no transaction at all. Need to "commit" the intermediate state.
CommitHelper();
if (this.finalState == null)
{
this.context.InstanceHandle.Free();
throw Fx.Exception.AsError(new InstanceHandleConflictException(null, this.context.InstanceView.InstanceId));
}
}
return true;
}
static void OnCommitWait(object state, TimeoutException exception)
{
ExecuteAsyncResult thisPtr = (ExecuteAsyncResult)state;
thisPtr.Complete(false, exception ?? thisPtr.AfterCommitWait());
}
Exception AfterCommitWait()
{
if (this.inDoubt)
{
this.context.InstanceHandle.Free();
return new TransactionInDoubtException(SRCore.TransactionInDoubtNonHost);
}
if (this.rolledBack)
{
if (this.context.IsHandleDoomedByRollback)
{
this.context.InstanceHandle.Free();
}
return new TransactionAbortedException(SRCore.TransactionRolledBackNonHost);
}
if (this.finalState == null)
{
this.context.InstanceHandle.Free();
return new InstanceHandleConflictException(null, this.context.InstanceView.InstanceId);
}
return null;
}
void CommitHelper()
{
this.finalState = this.context.InstanceHandle.Commit(this.context.InstanceView);
}
void SimpleCleanup(AsyncResult result, Exception exception)
{
if (this.initialInstanceHandle == null)
{
Fx.Assert(this.priorAsyncResult != null, "In the non-outer case, we should always have a priorAsyncResult here, since we set it before ----igining OnComplete.");
this.context.LastAsyncResult = this.priorAsyncResult;
}
if (exception != null)
{
if (this.context != null && this.context.IsHandleDoomedByRollback)
{
this.context.InstanceHandle.Free();
}
else if (exception is TimeoutException || exception is OperationCanceledException)
{
if (this.context == null)
{
this.initialInstanceHandle.Free();
}
else
{
this.context.InstanceHandle.Free();
}
}
}
}
void Cleanup(AsyncResult result, Exception exception)
{
try
{
SimpleCleanup(result, exception);
if (this.transactionToCommit != null)
{
try
{
this.transactionToCommit.Rollback(exception);
}
catch (TransactionException)
{
}
}
}
finally
{
Fx.AssertAndThrowFatal(this.context.Active, "Out-of-[....] between InstanceExecutionContext and ExecutionAsyncResult.");
this.context.LastAsyncResult = null;
this.context.RootAsyncResult = null;
this.context.InstanceHandle.ReleaseExecutionContext();
}
}
void ISinglePhaseNotification.SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
{
CommitHelper();
singlePhaseEnlistment.Committed();
this.waitForTransaction.Set();
}
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
CommitHelper();
enlistment.Done();
this.waitForTransaction.Set();
}
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
enlistment.Done();
this.inDoubt = true;
this.waitForTransaction.Set();
}
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
enlistment.Done();
this.rolledBack = true;
this.waitForTransaction.Set();
}
}
class BindReclaimedLockAsyncResult : AsyncResult
{
static Action<object, TimeoutException> waitComplete = new Action<object, TimeoutException>(OnWaitComplete);
readonly InstancePersistenceContext context;
public BindReclaimedLockAsyncResult(InstancePersistenceContext context, AsyncWaitHandle wait, TimeSpan timeout, AsyncCallback callback, object state)
: base(callback, state)
{
this.context = context;
if (wait.WaitAsync(BindReclaimedLockAsyncResult.waitComplete, this, timeout))
{
this.context.ConcludeBindReclaimedLockHelper();
Complete(true);
}
}
static void OnWaitComplete(object state, TimeoutException timeoutException)
{
BindReclaimedLockAsyncResult thisPtr = (BindReclaimedLockAsyncResult)state;
Exception completionException = null;
try
{
if (timeoutException != null)
{
thisPtr.context.InstanceHandle.CancelReclaim(new TimeoutException(SRCore.TimedOutWaitingForLockResolution));
}
thisPtr.context.ConcludeBindReclaimedLockHelper();
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
completionException = exception;
}
thisPtr.Complete(false, completionException);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<BindReclaimedLockAsyncResult>(result);
}
}
[Serializable]
class BindReclaimedLockException : Exception
{
public BindReclaimedLockException()
{
}
internal BindReclaimedLockException(AsyncWaitHandle markerWaitHandle)
: base(SRCore.BindReclaimedLockException)
{
MarkerWaitHandle = markerWaitHandle;
}
internal AsyncWaitHandle MarkerWaitHandle { get; private set; }
[SecurityCritical]
protected BindReclaimedLockException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}
}