1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Runtime.DurableInstancing
7 using System.Collections.Generic;
9 using System.Threading;
10 using System.Transactions;
11 using System.Xml.Linq;
12 using System.Runtime.Diagnostics;
14 [Fx.Tag.XamlVisible(false)]
15 public sealed class InstanceHandle
17 [Fx.Tag.SynchronizationObject(Blocking = false)]
18 readonly object thisLock = new object();
20 object providerObject;
21 bool providerObjectSet;
22 bool needFreedNotification;
23 PreparingEnlistment pendingPreparingEnlistment;
24 AcquireContextAsyncResult pendingRollback;
25 InstanceHandleReference inProgressBind;
26 WaitForEventsAsyncResult waitResult;
27 HashSet<XName> boundOwnerEvents;
28 HashSet<InstancePersistenceEvent> pendingOwnerEvents;
29 EventTraceActivity eventTraceActivity;
31 // Fields used to implement an atomic Guid Id get/set property.
33 volatile bool idIsSet;
35 internal InstanceHandle(InstanceStore store, InstanceOwner owner)
37 Fx.Assert(store != null, "Shouldn't be possible.");
42 View = new InstanceView(owner);
46 internal InstanceHandle(InstanceStore store, InstanceOwner owner, Guid instanceId)
48 Fx.Assert(store != null, "Shouldn't be possible here either.");
49 Fx.Assert(instanceId != Guid.Empty, "Should be validating this.");
55 View = new InstanceView(owner, instanceId);
57 if (Fx.Trace.IsEtwProviderEnabled)
59 eventTraceActivity = new EventTraceActivity(instanceId);
64 public bool IsValid { get; private set; }
67 internal InstanceView View { get; private set; }
68 internal InstanceStore Store { get; private set; }
70 internal InstanceOwner Owner { get; private set; }
72 // Since writing to a Guid field is not atomic, we need synchronization between reading and writing. The idIsSet boolean field can only
73 // appear true once the id field is completely written due to the memory barriers implied by the reads and writes to a volatile field.
74 // Writes to bool fields are atomic, and this property is only written to once. By checking the bool prior to reading the Guid, we can
75 // be sure that the Guid is fully materialized when read.
80 // this.idIsSet is volatile.
90 Fx.Assert(value != Guid.Empty, "Cannot set an empty Id.");
91 Fx.Assert(this.id == Guid.Empty, "Cannot set Id more than once.");
92 Fx.Assert(!this.idIsSet, "idIsSet out of sync with id.");
96 if (Fx.Trace.IsEtwProviderEnabled)
98 eventTraceActivity = new EventTraceActivity(value);
101 // this.isIdSet is volatile.
106 internal long Version { get; private set; }
108 internal InstanceHandle ConflictingHandle { get; set; }
110 internal object ProviderObject
114 return this.providerObject;
118 this.providerObject = value;
119 this.providerObjectSet = true;
123 internal EventTraceActivity EventTraceActivity
127 return this.eventTraceActivity;
131 // When non-null, a transaction is pending.
132 AcquireContextAsyncResult CurrentTransactionalAsyncResult { get; set; }
134 bool OperationPending { get; set; }
135 bool TooLateToEnlist { get; set; }
136 AcquireContextAsyncResult AcquirePending { get; set; }
137 InstancePersistenceContext CurrentExecutionContext { get; set; }
143 return this.thisLock;
150 if (!this.providerObjectSet)
152 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.HandleFreedBeforeInitialized));
160 List<InstanceHandleReference> handlesPendingResolution = null;
161 WaitForEventsAsyncResult resultToCancel = null;
165 bool needNotification = false;
166 InstancePersistenceContext currentContext = null;
176 IEnumerable<XName> eventsToUnbind = null;
177 if (this.pendingOwnerEvents != null && this.pendingOwnerEvents.Count > 0)
179 eventsToUnbind = this.pendingOwnerEvents.Select(persistenceEvent => persistenceEvent.Name);
181 if (this.boundOwnerEvents != null && this.boundOwnerEvents.Count > 0)
183 eventsToUnbind = eventsToUnbind == null ? this.boundOwnerEvents : eventsToUnbind.Concat(this.boundOwnerEvents);
185 if (eventsToUnbind != null)
187 Fx.Assert(Owner != null, "How do we have owner events without an owner.");
188 Store.RemoveHandleFromEvents(this, eventsToUnbind, Owner);
190 if (this.waitResult != null)
192 resultToCancel = this.waitResult;
193 this.waitResult = null;
196 if (OperationPending)
198 if (AcquirePending != null)
200 // If in this stage, we need to short-circuit the pending transaction.
201 Fx.Assert(CurrentTransactionalAsyncResult != null, "Should have a pending transaction if we are waiting for it.");
202 CurrentTransactionalAsyncResult.WaitForHostTransaction.Set();
203 this.needFreedNotification = true;
207 // Here, just notify the currently executing command.
208 Fx.Assert(CurrentExecutionContext != null, "Must have either this or AcquirePending set.");
209 currentContext = CurrentExecutionContext;
214 needNotification = true;
216 if (this.inProgressBind != null)
218 Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution);
220 else if (Version != -1)
222 // This means the handle was successfully bound in the past. Need to remove it from the table of handles.
228 if (currentContext != null)
230 // Need to do this not in a lock.
231 currentContext.NotifyHandleFree();
235 if (OperationPending)
237 this.needFreedNotification = true;
239 // Cancel any pending lock reclaim here.
240 if (this.inProgressBind != null)
242 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim.");
244 // Null reason defaults to OperationCanceledException. (Defer creating it since this might not be a
245 // reclaim attempt, but we don't know until we take the HandlesLock.)
246 Owner.FaultBind(ref this.inProgressBind, ref handlesPendingResolution, null);
251 needNotification = true;
256 if (needNotification)
258 Store.FreeInstanceHandle(this, ProviderObject);
263 if (resultToCancel != null)
265 resultToCancel.Canceled();
268 InstanceOwner.ResolveHandles(handlesPendingResolution);
272 internal void BindOwnerEvent(InstancePersistenceEvent persistenceEvent)
276 Fx.Assert(OperationPending, "Should only be called during an operation.");
277 Fx.Assert(AcquirePending == null, "Should only be called after acquiring the transaction.");
278 Fx.Assert(Owner != null, "Must be bound to owner to have an owner-scoped event.");
280 if (IsValid && (this.boundOwnerEvents == null || !this.boundOwnerEvents.Contains(persistenceEvent.Name)))
282 if (this.pendingOwnerEvents == null)
284 this.pendingOwnerEvents = new HashSet<InstancePersistenceEvent>();
286 else if (this.pendingOwnerEvents.Contains(persistenceEvent))
290 this.pendingOwnerEvents.Add(persistenceEvent);
291 Store.PendHandleToEvent(this, persistenceEvent, Owner);
296 internal void StartPotentialBind()
300 Fx.AssertAndThrow(Version == -1, "Handle already bound to a lock.");
302 Fx.Assert(OperationPending, "Should only be called during an operation.");
303 Fx.Assert(AcquirePending == null, "Should only be called after acquiring the transaction.");
304 Fx.Assert(this.inProgressBind == null, "StartPotentialBind should only be called once per command.");
305 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock.");
307 Owner.StartBind(this, ref this.inProgressBind);
311 internal void BindOwner(InstanceOwner owner)
313 Fx.Assert(owner != null, "Null owner passed to BindOwner.");
317 Fx.Assert(this.inProgressBind == null, "How did we get a bind in progress without an owner?");
319 Fx.Assert(Owner == null, "BindOwner called when we already have an owner.");
324 internal void BindInstance(Guid instanceId)
326 Fx.Assert(instanceId != Guid.Empty, "BindInstance called with empty Guid.");
328 List<InstanceHandleReference> handlesPendingResolution = null;
333 Fx.Assert(Id == Guid.Empty, "Instance already boud in BindInstance.");
336 Fx.Assert(OperationPending, "BindInstance should only be called during an operation.");
337 Fx.Assert(AcquirePending == null, "BindInstance should only be called after acquiring the transaction.");
338 if (this.inProgressBind != null)
340 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock.");
341 Owner.InstanceBound(ref this.inProgressBind, ref handlesPendingResolution);
347 InstanceOwner.ResolveHandles(handlesPendingResolution);
351 internal void Bind(long instanceVersion)
353 Fx.AssertAndThrow(instanceVersion >= 0, "Negative instanceVersion passed to Bind.");
354 Fx.Assert(Owner != null, "Bind called before owner bound.");
355 Fx.Assert(Id != Guid.Empty, "Bind called before instance bound.");
359 Fx.AssertAndThrow(Version == -1, "This should only be reachable once per handle.");
360 Version = instanceVersion;
362 Fx.Assert(OperationPending, "Bind should only be called during an operation.");
363 Fx.Assert(AcquirePending == null, "Bind should only be called after acquiring the transaction.");
364 if (this.inProgressBind == null)
366 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.BindLockRequiresCommandFlag));
371 // Returns null if an InstanceHandleConflictException should be thrown.
372 internal AsyncWaitHandle StartReclaim(long instanceVersion)
374 List<InstanceHandleReference> handlesPendingResolution = null;
379 Fx.AssertAndThrow(Version == -1, "StartReclaim should only be reachable if the lock hasn't been bound.");
381 Fx.Assert(OperationPending, "StartReclaim should only be called during an operation.");
382 Fx.Assert(AcquirePending == null, "StartReclaim should only be called after acquiring the transaction.");
383 if (this.inProgressBind == null)
385 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.BindLockRequiresCommandFlag));
388 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in StartReclaim.");
389 return Owner.InitiateLockResolution(instanceVersion, ref this.inProgressBind, ref handlesPendingResolution);
394 InstanceOwner.ResolveHandles(handlesPendingResolution);
398 // After calling this method, the caller doesn't need to wait for the wait handle to become set (but they can).
399 internal void CancelReclaim(Exception reason)
401 List<InstanceHandleReference> handlesPendingResolution = null;
406 if (this.inProgressBind == null)
408 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.DoNotCompleteTryCommandWithPendingReclaim));
411 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim.");
412 Owner.FaultBind(ref this.inProgressBind, ref handlesPendingResolution, reason);
417 InstanceOwner.ResolveHandles(handlesPendingResolution);
421 // Returns the false if an InstanceHandleConflictException should be thrown.
422 internal bool FinishReclaim(ref long instanceVersion)
424 List<InstanceHandleReference> handlesPendingResolution = null;
429 if (this.inProgressBind == null)
431 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.DoNotCompleteTryCommandWithPendingReclaim));
434 Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim.");
435 if (!Owner.FinishBind(ref this.inProgressBind, ref instanceVersion, ref handlesPendingResolution))
440 Fx.AssertAndThrow(Version == -1, "Should only be able to set the version once per handle.");
441 Fx.AssertAndThrow(instanceVersion >= 0, "Incorrect version resulting from conflict resolution.");
442 Version = instanceVersion;
448 InstanceOwner.ResolveHandles(handlesPendingResolution);
452 [Fx.Tag.Blocking(CancelMethod = "Free")]
453 internal InstancePersistenceContext AcquireExecutionContext(Transaction hostTransaction, TimeSpan timeout)
455 bool setOperationPending = false;
456 InstancePersistenceContext result = null;
459 result = AcquireContextAsyncResult.End(new AcquireContextAsyncResult(this, hostTransaction, timeout, out setOperationPending));
460 Fx.AssertAndThrow(result != null, "Null result returned from AcquireContextAsyncResult (synchronous).");
465 if (result == null && setOperationPending)
472 internal IAsyncResult BeginAcquireExecutionContext(Transaction hostTransaction, TimeSpan timeout, AsyncCallback callback, object state)
474 bool setOperationPending = false;
475 IAsyncResult result = null;
478 result = new AcquireContextAsyncResult(this, hostTransaction, timeout, out setOperationPending, callback, state);
483 if (result == null && setOperationPending)
490 [Fx.Tag.Blocking(CancelMethod = "Free", Conditional = "!result.IsCompleted")]
491 internal InstancePersistenceContext EndAcquireExecutionContext(IAsyncResult result)
493 return AcquireContextAsyncResult.End(result);
496 internal void ReleaseExecutionContext()
498 Fx.Assert(OperationPending, "ReleaseExecutionContext called with no operation pending.");
502 // Returns null if an InstanceHandleConflictException should be thrown.
503 internal InstanceView Commit(InstanceView newState)
505 Fx.Assert(newState != null, "Null view passed to Commit.");
506 newState.MakeReadOnly();
509 List<InstanceHandleReference> handlesPendingResolution = null;
510 InstanceHandle handleToFree = null;
511 List<InstancePersistenceEvent> normals = null;
512 WaitForEventsAsyncResult resultToComplete = null;
517 if (this.inProgressBind != null)
519 // If there's a Version, it should be committed.
522 if (!Owner.TryCompleteBind(ref this.inProgressBind, ref handlesPendingResolution, out handleToFree))
529 Fx.Assert(OperationPending, "Should have cancelled this bind in FinishOperation.");
530 Fx.Assert(AcquirePending == null, "Should not be in Commit during AcquirePending.");
531 Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution);
535 if (this.pendingOwnerEvents != null && IsValid)
537 if (this.boundOwnerEvents == null)
539 this.boundOwnerEvents = new HashSet<XName>();
542 foreach (InstancePersistenceEvent persistenceEvent in this.pendingOwnerEvents)
544 if (!this.boundOwnerEvents.Add(persistenceEvent.Name))
546 Fx.Assert("Should not have conflicts between pending and bound events.");
550 InstancePersistenceEvent normal = Store.AddHandleToEvent(this, persistenceEvent, Owner);
555 normals = new List<InstancePersistenceEvent>(this.pendingOwnerEvents.Count);
561 this.pendingOwnerEvents = null;
563 if (normals != null && this.waitResult != null)
565 resultToComplete = this.waitResult;
566 this.waitResult = null;
575 InstanceOwner.ResolveHandles(handlesPendingResolution);
577 // This is a convenience, it is not required for correctness.
578 if (handleToFree != null)
580 Fx.Assert(!object.ReferenceEquals(handleToFree, this), "Shouldn't have been told to free ourselves.");
584 if (resultToComplete != null)
586 resultToComplete.Signaled(normals);
591 void OnPrepare(PreparingEnlistment preparingEnlistment)
593 bool prepareNeeded = false;
598 // Skip this if somehow we already got rolled back or committed.
601 TooLateToEnlist = true;
602 if (OperationPending && AcquirePending == null)
604 Fx.Assert(CurrentExecutionContext != null, "Should either be acquiring or executing in Prepare.");
605 this.pendingPreparingEnlistment = preparingEnlistment;
609 prepareNeeded = true;
614 preparingEnlistment.Prepared();
618 void OnRollBack(AcquireContextAsyncResult rollingBack)
620 bool rollbackNeeded = false;
623 TooLateToEnlist = true;
624 if (OperationPending && AcquirePending == null)
626 Fx.Assert(CurrentExecutionContext != null, "Should either be acquiring or executing in RollBack.");
627 this.pendingRollback = rollingBack;
629 // Don't prepare and roll back.
630 this.pendingPreparingEnlistment = null;
634 rollbackNeeded = true;
639 rollingBack.RollBack();
643 void FinishOperation()
645 List<InstanceHandleReference> handlesPendingResolution = null;
648 bool needNotification;
649 PreparingEnlistment preparingEnlistment;
650 AcquireContextAsyncResult pendingRollback;
653 OperationPending = false;
654 AcquirePending = null;
655 CurrentExecutionContext = null;
657 // This means we could have bound the handle, but didn't - clear the state here.
658 if (this.inProgressBind != null && (Version == -1 || !IsValid))
660 Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution);
662 else if (Version != -1 && !IsValid)
664 // This means the handle was successfully bound in the past. Need to remove it from the table of handles.
668 needNotification = this.needFreedNotification;
669 this.needFreedNotification = false;
671 preparingEnlistment = this.pendingPreparingEnlistment;
672 this.pendingPreparingEnlistment = null;
674 pendingRollback = this.pendingRollback;
675 this.pendingRollback = null;
679 if (needNotification)
681 Store.FreeInstanceHandle(this, ProviderObject);
686 if (pendingRollback != null)
688 Fx.Assert(preparingEnlistment == null, "Should not have both.");
689 pendingRollback.RollBack();
691 else if (preparingEnlistment != null)
693 preparingEnlistment.Prepared();
699 InstanceOwner.ResolveHandles(handlesPendingResolution);
703 List<InstancePersistenceEvent> StartWaiting(WaitForEventsAsyncResult result, IOThreadTimer timeoutTimer, TimeSpan timeout)
707 if (this.waitResult != null)
709 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.WaitAlreadyInProgress));
713 throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed));
716 if (this.boundOwnerEvents != null && this.boundOwnerEvents.Count > 0)
718 Fx.Assert(Owner != null, "How do we have owner events without an owner.");
719 List<InstancePersistenceEvent> readyEvents = Store.SelectSignaledEvents(this.boundOwnerEvents, Owner);
720 if (readyEvents != null)
722 Fx.Assert(readyEvents.Count != 0, "Should not return a zero-length list.");
727 this.waitResult = result;
729 // This is done here to be under the lock. That way it doesn't get canceled before it is set.
730 if (timeoutTimer != null)
732 timeoutTimer.Set(timeout);
739 bool CancelWaiting(WaitForEventsAsyncResult result)
743 Fx.Assert(result != null, "Null result passed to CancelWaiting.");
744 if (!object.ReferenceEquals(this.waitResult, result))
748 this.waitResult = null;
753 internal void EventReady(InstancePersistenceEvent persistenceEvent)
755 WaitForEventsAsyncResult resultToComplete = null;
758 if (this.waitResult != null)
760 resultToComplete = this.waitResult;
761 this.waitResult = null;
765 if (resultToComplete != null)
767 resultToComplete.Signaled(persistenceEvent);
771 internal static IAsyncResult BeginWaitForEvents(InstanceHandle handle, TimeSpan timeout, AsyncCallback callback, object state)
773 return new WaitForEventsAsyncResult(handle, timeout, callback, state);
776 internal static List<InstancePersistenceEvent> EndWaitForEvents(IAsyncResult result)
778 return WaitForEventsAsyncResult.End(result);
781 class AcquireContextAsyncResult : AsyncResult, IEnlistmentNotification
783 static Action<object, TimeoutException> onHostTransaction = new Action<object, TimeoutException>(OnHostTransaction);
785 readonly InstanceHandle handle;
786 readonly TimeoutHelper timeoutHelper;
788 InstancePersistenceContext executionContext;
790 public AcquireContextAsyncResult(InstanceHandle handle, Transaction hostTransaction, TimeSpan timeout, out bool setOperationPending, AsyncCallback callback, object state)
791 : this(handle, hostTransaction, timeout, out setOperationPending, false, callback, state)
795 [Fx.Tag.Blocking(CancelMethod = "Free", CancelDeclaringType = typeof(InstanceHandle))]
796 public AcquireContextAsyncResult(InstanceHandle handle, Transaction hostTransaction, TimeSpan timeout, out bool setOperationPending)
797 : this(handle, hostTransaction, timeout, out setOperationPending, true, null, null)
801 [Fx.Tag.Blocking(CancelMethod = "Free", CancelDeclaringType = typeof(InstanceHandle), Conditional = "synchronous")]
802 AcquireContextAsyncResult(InstanceHandle handle, Transaction hostTransaction, TimeSpan timeout, out bool setOperationPending, bool synchronous, AsyncCallback callback, object state)
803 : base(callback, state)
805 // Need to report back to the caller whether or not we set OperationPending.
806 setOperationPending = false;
808 this.handle = handle;
809 HostTransaction = hostTransaction;
810 this.timeoutHelper = new TimeoutHelper(timeout);
812 AcquireContextAsyncResult transactionWait;
813 bool reuseContext = false;
814 lock (this.handle.ThisLock)
816 if (!this.handle.IsValid)
818 throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed));
821 if (this.handle.OperationPending)
823 throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CommandExecutionCannotOverlap));
825 setOperationPending = true;
826 this.handle.OperationPending = true;
828 transactionWait = this.handle.CurrentTransactionalAsyncResult;
829 if (transactionWait != null)
831 Fx.Assert(this.handle.AcquirePending == null, "Overlapped acquires pending.");
833 // If the transaction matches but is already completed (or completing), the easiest ting to do
834 // is wait for it to complete, then try to re-enlist, and have that failure be the failure mode for Execute.
835 // We do that by following the regular, non-matching transaction path.
836 if (transactionWait.HostTransaction.Equals(hostTransaction) && !this.handle.TooLateToEnlist)
839 this.executionContext = transactionWait.ReuseContext();
840 this.handle.CurrentExecutionContext = this.executionContext;
844 this.handle.AcquirePending = this;
849 if (transactionWait != null)
851 Fx.Assert(transactionWait.IsCompleted, "Old AsyncResult must be completed by now.");
853 // Reuse the existing InstanceExecutionContext if this is the same transaction we're waiting for.
860 TimeSpan waitTimeout = this.timeoutHelper.RemainingTime();
863 if (!transactionWait.WaitForHostTransaction.Wait(waitTimeout))
865 throw Fx.Exception.AsError(new TimeoutException(InternalSR.TimeoutOnOperation(waitTimeout)));
870 if (!transactionWait.WaitForHostTransaction.WaitAsync(AcquireContextAsyncResult.onHostTransaction, this, waitTimeout))
877 if (DoAfterTransaction())
883 public Transaction HostTransaction { get; private set; }
884 public AsyncWaitHandle WaitForHostTransaction { get; private set; }
886 public static InstancePersistenceContext End(IAsyncResult result)
888 AcquireContextAsyncResult pThis = AsyncResult.End<AcquireContextAsyncResult>(result);
889 Fx.Assert(pThis.executionContext != null, "Somehow the execution context didn't get set.");
890 return pThis.executionContext;
893 internal void RollBack()
895 if (this.executionContext.IsHandleDoomedByRollback)
901 Fx.Assert(this.handle.inProgressBind == null, "Either this should have been bound to a lock, hence dooming the handle by rollback, or this should have been cancelled in FinishOperation.");
902 Fx.Assert(this.handle.pendingOwnerEvents == null, "Either this should have doomed the handle or already been committed.");
903 WaitForHostTransaction.Set();
907 static void OnHostTransaction(object state, TimeoutException timeoutException)
909 AcquireContextAsyncResult pThis = (AcquireContextAsyncResult)state;
910 Exception exception = timeoutException;
911 bool completeSelf = exception != null;
916 if (pThis.DoAfterTransaction())
933 if (exception != null)
935 pThis.handle.FinishOperation();
937 pThis.Complete(false, exception);
941 bool DoAfterTransaction()
943 AcquireContextAsyncResult setWaitTo = null;
946 lock (this.handle.ThisLock)
948 if (!this.handle.IsValid)
950 throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed));
953 if (HostTransaction == null)
955 this.executionContext = new InstancePersistenceContext(this.handle, this.timeoutHelper.RemainingTime());
959 this.executionContext = new InstancePersistenceContext(this.handle, HostTransaction);
962 this.handle.AcquirePending = null;
963 this.handle.CurrentExecutionContext = this.executionContext;
964 this.handle.TooLateToEnlist = false;
967 if (HostTransaction != null)
969 WaitForHostTransaction = new AsyncWaitHandle(EventResetMode.ManualReset);
970 HostTransaction.EnlistVolatile(this, EnlistmentOptions.None);
976 this.handle.CurrentTransactionalAsyncResult = setWaitTo;
982 InstancePersistenceContext ReuseContext()
984 Fx.Assert(this.executionContext != null, "ReuseContext called but there is no context.");
986 this.executionContext.PrepareForReuse();
987 return this.executionContext;
990 void IEnlistmentNotification.Commit(Enlistment enlistment)
992 Fx.AssertAndThrow(this.handle.CurrentExecutionContext == null, "Prepare should have been called first and waited until after command processing.");
994 bool commitSuccessful = this.handle.Commit(this.executionContext.InstanceView) != null;
996 if (commitSuccessful)
998 WaitForHostTransaction.Set();
1006 void IEnlistmentNotification.InDoubt(Enlistment enlistment)
1012 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
1014 this.handle.OnPrepare(preparingEnlistment);
1017 void IEnlistmentNotification.Rollback(Enlistment enlistment)
1020 this.handle.OnRollBack(this);
1024 class WaitForEventsAsyncResult : AsyncResult
1026 static readonly Action<object> timeoutCallback = new Action<object>(OnTimeout);
1028 readonly InstanceHandle handle;
1029 readonly TimeSpan timeout;
1031 IOThreadTimer timer;
1033 List<InstancePersistenceEvent> readyEvents;
1035 internal WaitForEventsAsyncResult(InstanceHandle handle, TimeSpan timeout, AsyncCallback callback, object state)
1036 : base(callback, state)
1038 this.handle = handle;
1039 this.timeout = timeout;
1041 if (this.timeout != TimeSpan.Zero && this.timeout != TimeSpan.MaxValue)
1043 this.timer = new IOThreadTimer(WaitForEventsAsyncResult.timeoutCallback, this, false);
1046 List<InstancePersistenceEvent> existingReadyEvents = this.handle.StartWaiting(this, this.timer, this.timeout);
1047 if (existingReadyEvents == null)
1049 if (this.timeout == TimeSpan.Zero)
1051 this.handle.CancelWaiting(this);
1052 throw Fx.Exception.AsError(new TimeoutException(SRCore.WaitForEventsTimedOut(TimeSpan.Zero)));
1057 this.readyEvents = existingReadyEvents;
1062 internal void Signaled(InstancePersistenceEvent persistenceEvent)
1064 Signaled(new List<InstancePersistenceEvent>(1) { persistenceEvent });
1067 internal void Signaled(List<InstancePersistenceEvent> persistenceEvents)
1069 if (this.timer != null)
1071 this.timer.Cancel();
1073 this.readyEvents = persistenceEvents;
1077 internal void Canceled()
1079 if (this.timer != null)
1081 this.timer.Cancel();
1083 Complete(false, new OperationCanceledException(SRCore.HandleFreed));
1086 static void OnTimeout(object state)
1088 WaitForEventsAsyncResult thisPtr = (WaitForEventsAsyncResult)state;
1089 if (thisPtr.handle.CancelWaiting(thisPtr))
1091 thisPtr.Complete(false, new TimeoutException(SRCore.WaitForEventsTimedOut(thisPtr.timeout)));
1095 internal static List<InstancePersistenceEvent> End(IAsyncResult result)
1097 return AsyncResult.End<WaitForEventsAsyncResult>(result).readyEvents;