Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / System.Runtime.DurableInstancing / System / Runtime / DurableInstancing / InstanceHandle.cs / 1477082 / InstanceHandle.cs
//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Runtime.DurableInstancing { using System.Collections.Generic; using System.Linq; using System.Threading; using System.Transactions; using System.Xml.Linq; [Fx.Tag.XamlVisible(false)] public sealed class InstanceHandle { [Fx.Tag.SynchronizationObject(Blocking=false)] readonly object thisLock = new object(); object providerObject; bool providerObjectSet; bool needFreedNotification; PreparingEnlistment pendingPreparingEnlistment; AcquireContextAsyncResult pendingRollback; InstanceHandleReference inProgressBind; WaitForEventsAsyncResult waitResult; HashSetboundOwnerEvents; HashSet pendingOwnerEvents; // Fields used to implement an atomic Guid Id get/set property. Guid id; volatile bool idIsSet; internal InstanceHandle(InstanceStore store, InstanceOwner owner) { Fx.Assert(store != null, "Shouldn't be possible."); Version = -1; Store = store; Owner = owner; View = new InstanceView(owner); IsValid = true; } internal InstanceHandle(InstanceStore store, InstanceOwner owner, Guid instanceId) { Fx.Assert(store != null, "Shouldn't be possible here either."); Fx.Assert(instanceId != Guid.Empty, "Should be validating this."); Version = -1; Store = store; Owner = owner; Id = instanceId; View = new InstanceView(owner, instanceId); IsValid = true; } public bool IsValid { get; private set; } internal InstanceView View { get; private set; } internal InstanceStore Store { get; private set; } internal InstanceOwner Owner { get; private set; } // Since writing to a Guid field is not atomic, we need synchronization between reading and writing. The idIsSet boolean field can only // appear true once the id field is completely written due to the memory barriers implied by the reads and writes to a volatile field. // 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 // be sure that the Guid is fully materialized when read. internal Guid Id { get { // this.idIsSet is volatile. if (!this.idIsSet) { return Guid.Empty; } return this.id; } private set { Fx.Assert(value != Guid.Empty, "Cannot set an empty Id."); Fx.Assert(this.id == Guid.Empty, "Cannot set Id more than once."); Fx.Assert(!this.idIsSet, "idIsSet out of [....] with id."); this.id = value; // this.isIdSet is volatile. this.idIsSet = true; } } internal long Version { get; private set; } internal InstanceHandle ConflictingHandle { get; set; } internal object ProviderObject { get { return this.providerObject; } set { this.providerObject = value; this.providerObjectSet = true; } } // When non-null, a transaction is pending. AcquireContextAsyncResult CurrentTransactionalAsyncResult { get; set; } bool OperationPending { get; set; } bool TooLateToEnlist { get; set; } AcquireContextAsyncResult AcquirePending { get; set; } InstancePersistenceContext CurrentExecutionContext { get; set; } object ThisLock { get { return this.thisLock; } } public void Free() { if (!this.providerObjectSet) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.HandleFreedBeforeInitialized)); } if (!IsValid) { return; } List handlesPendingResolution = null; WaitForEventsAsyncResult resultToCancel = null; try { bool needNotification = false; InstancePersistenceContext currentContext = null; lock (ThisLock) { if (!IsValid) { return; } IsValid = false; IEnumerable eventsToUnbind = null; if (this.pendingOwnerEvents != null && this.pendingOwnerEvents.Count > 0) { eventsToUnbind = this.pendingOwnerEvents.Select(persistenceEvent => persistenceEvent.Name); } if (this.boundOwnerEvents != null && this.boundOwnerEvents.Count > 0) { eventsToUnbind = eventsToUnbind == null ? this.boundOwnerEvents : eventsToUnbind.Concat(this.boundOwnerEvents); } if (eventsToUnbind != null) { Fx.Assert(Owner != null, "How do we have owner events without an owner."); Store.RemoveHandleFromEvents(this, eventsToUnbind, Owner); } if (this.waitResult != null) { resultToCancel = this.waitResult; this.waitResult = null; } if (OperationPending) { if (AcquirePending != null) { // If in this stage, we need to short-circuit the pending transaction. Fx.Assert(CurrentTransactionalAsyncResult != null, "Should have a pending transaction if we are waiting for it."); CurrentTransactionalAsyncResult.WaitForHostTransaction.Set(); this.needFreedNotification = true; } else { // Here, just notify the currently executing command. Fx.Assert(CurrentExecutionContext != null, "Must have either this or AcquirePending set."); currentContext = CurrentExecutionContext; } } else { needNotification = true; if (this.inProgressBind != null) { Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution); } else if (Version != -1) { // This means the handle was successfully bound in the past. Need to remove it from the table of handles. Owner.Unbind(this); } } } if (currentContext != null) { // Need to do this not in a lock. currentContext.NotifyHandleFree(); lock (ThisLock) { if (OperationPending) { this.needFreedNotification = true; // Cancel any pending lock reclaim here. if (this.inProgressBind != null) { Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim."); // Null reason defaults to OperationCanceledException. (Defer creating it since this might not be a // reclaim attempt, but we don't know until we take the HandlesLock.) Owner.FaultBind(ref this.inProgressBind, ref handlesPendingResolution, null); } } else { needNotification = true; } } } if (needNotification) { Store.FreeInstanceHandle(this, ProviderObject); } } finally { if (resultToCancel != null) { resultToCancel.Canceled(); } InstanceOwner.ResolveHandles(handlesPendingResolution); } } internal void BindOwnerEvent(InstancePersistenceEvent persistenceEvent) { lock (ThisLock) { Fx.Assert(OperationPending, "Should only be called during an operation."); Fx.Assert(AcquirePending == null, "Should only be called after acquiring the transaction."); Fx.Assert(Owner != null, "Must be bound to owner to have an owner-scoped event."); if (IsValid && (this.boundOwnerEvents == null || !this.boundOwnerEvents.Contains(persistenceEvent.Name))) { if (this.pendingOwnerEvents == null) { this.pendingOwnerEvents = new HashSet (); } else if (this.pendingOwnerEvents.Contains(persistenceEvent)) { return; } this.pendingOwnerEvents.Add(persistenceEvent); Store.PendHandleToEvent(this, persistenceEvent, Owner); } } } internal void StartPotentialBind() { lock (ThisLock) { Fx.AssertAndThrow(Version == -1, "Handle already bound to a lock."); Fx.Assert(OperationPending, "Should only be called during an operation."); Fx.Assert(AcquirePending == null, "Should only be called after acquiring the transaction."); Fx.Assert(this.inProgressBind == null, "StartPotentialBind should only be called once per command."); Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock."); Owner.StartBind(this, ref this.inProgressBind); } } internal void BindOwner(InstanceOwner owner) { Fx.Assert(owner != null, "Null owner passed to BindOwner."); lock (ThisLock) { Fx.Assert(this.inProgressBind == null, "How did we get a bind in progress without an owner?"); Fx.Assert(Owner == null, "BindOwner called when we already have an owner."); Owner = owner; } } internal void BindInstance(Guid instanceId) { Fx.Assert(instanceId != Guid.Empty, "BindInstance called with empty Guid."); List handlesPendingResolution = null; try { lock (ThisLock) { Fx.Assert(Id == Guid.Empty, "Instance already boud in BindInstance."); Id = instanceId; Fx.Assert(OperationPending, "BindInstance should only be called during an operation."); Fx.Assert(AcquirePending == null, "BindInstance should only be called after acquiring the transaction."); if (this.inProgressBind != null) { Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock."); Owner.InstanceBound(ref this.inProgressBind, ref handlesPendingResolution); } } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); } } internal void Bind(long instanceVersion) { Fx.AssertAndThrow(instanceVersion >=0, "Negative instanceVersion passed to Bind."); Fx.Assert(Owner != null, "Bind called before owner bound."); Fx.Assert(Id != Guid.Empty, "Bind called before instance bound."); lock (ThisLock) { Fx.AssertAndThrow(Version == -1, "This should only be reachable once per handle."); Version = instanceVersion; Fx.Assert(OperationPending, "Bind should only be called during an operation."); Fx.Assert(AcquirePending == null, "Bind should only be called after acquiring the transaction."); if (this.inProgressBind == null) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.BindLockRequiresCommandFlag)); } } } // Returns null if an InstanceHandleConflictException should be thrown. internal AsyncWaitHandle StartReclaim(long instanceVersion) { List handlesPendingResolution = null; try { lock (ThisLock) { Fx.AssertAndThrow(Version == -1, "StartReclaim should only be reachable if the lock hasn't been bound."); Fx.Assert(OperationPending, "StartReclaim should only be called during an operation."); Fx.Assert(AcquirePending == null, "StartReclaim should only be called after acquiring the transaction."); if (this.inProgressBind == null) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.BindLockRequiresCommandFlag)); } Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in StartReclaim."); return Owner.InitiateLockResolution(instanceVersion, ref this.inProgressBind, ref handlesPendingResolution); } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); } } // After calling this method, the caller doesn't need to wait for the wait handle to become set (but they can). internal void CancelReclaim(Exception reason) { List handlesPendingResolution = null; try { lock (ThisLock) { if (this.inProgressBind == null) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.DoNotCompleteTryCommandWithPendingReclaim)); } Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim."); Owner.FaultBind(ref this.inProgressBind, ref handlesPendingResolution, reason); } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); } } // Returns the false if an InstanceHandleConflictException should be thrown. internal bool FinishReclaim(ref long instanceVersion) { List handlesPendingResolution = null; try { lock (ThisLock) { if (this.inProgressBind == null) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.DoNotCompleteTryCommandWithPendingReclaim)); } Fx.Assert(Owner != null, "Must be bound to owner to have an inProgressBind for the lock in CancelReclaim."); if (!Owner.FinishBind(ref this.inProgressBind, ref instanceVersion, ref handlesPendingResolution)) { return false; } Fx.AssertAndThrow(Version == -1, "Should only be able to set the version once per handle."); Fx.AssertAndThrow(instanceVersion >= 0, "Incorrect version resulting from conflict resolution."); Version = instanceVersion; return true; } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); } } [Fx.Tag.Blocking(CancelMethod = "Free")] internal InstancePersistenceContext AcquireExecutionContext(Transaction hostTransaction, TimeSpan timeout) { bool setOperationPending = false; InstancePersistenceContext result = null; try { result = AcquireContextAsyncResult.End(new AcquireContextAsyncResult(this, hostTransaction, timeout, out setOperationPending)); Fx.AssertAndThrow(result != null, "Null result returned from AcquireContextAsyncResult (synchronous)."); return result; } finally { if (result == null && setOperationPending) { FinishOperation(); } } } internal IAsyncResult BeginAcquireExecutionContext(Transaction hostTransaction, TimeSpan timeout, AsyncCallback callback, object state) { bool setOperationPending = false; IAsyncResult result = null; try { result = new AcquireContextAsyncResult(this, hostTransaction, timeout, out setOperationPending, callback, state); return result; } finally { if (result == null && setOperationPending) { FinishOperation(); } } } [Fx.Tag.Blocking(CancelMethod = "Free", Conditional = "!result.IsCompleted")] internal InstancePersistenceContext EndAcquireExecutionContext(IAsyncResult result) { return AcquireContextAsyncResult.End(result); } internal void ReleaseExecutionContext() { Fx.Assert(OperationPending, "ReleaseExecutionContext called with no operation pending."); FinishOperation(); } // Returns null if an InstanceHandleConflictException should be thrown. internal InstanceView Commit(InstanceView newState) { Fx.Assert(newState != null, "Null view passed to Commit."); newState.MakeReadOnly(); View = newState; List handlesPendingResolution = null; InstanceHandle handleToFree = null; List normals = null; WaitForEventsAsyncResult resultToComplete = null; try { lock (ThisLock) { if (this.inProgressBind != null) { // If there's a Version, it should be committed. if (Version != -1) { if (!Owner.TryCompleteBind(ref this.inProgressBind, ref handlesPendingResolution, out handleToFree)) { return null; } } else { Fx.Assert(OperationPending, "Should have cancelled this bind in FinishOperation."); Fx.Assert(AcquirePending == null, "Should not be in Commit during AcquirePending."); Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution); } } if (this.pendingOwnerEvents != null && IsValid) { if (this.boundOwnerEvents == null) { this.boundOwnerEvents = new HashSet (); } foreach (InstancePersistenceEvent persistenceEvent in this.pendingOwnerEvents) { if (!this.boundOwnerEvents.Add(persistenceEvent.Name)) { Fx.Assert("Should not have conflicts between pending and bound events."); continue; } InstancePersistenceEvent normal = Store.AddHandleToEvent(this, persistenceEvent, Owner); if (normal != null) { if (normals == null) { normals = new List (this.pendingOwnerEvents.Count); } normals.Add(normal); } } this.pendingOwnerEvents = null; if (normals != null && this.waitResult != null) { resultToComplete = this.waitResult; this.waitResult = null; } } return View; } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); // This is a convenience, it is not required for correctness. if (handleToFree != null) { Fx.Assert(!object.ReferenceEquals(handleToFree, this), "Shouldn't have been told to free ourselves."); handleToFree.Free(); } if (resultToComplete != null) { resultToComplete.Signaled(normals); } } } void OnPrepare(PreparingEnlistment preparingEnlistment) { bool prepareNeeded = false; lock (ThisLock) { if (TooLateToEnlist) { // Skip this if somehow we already got rolled back or committed. return; } TooLateToEnlist = true; if (OperationPending && AcquirePending == null) { Fx.Assert(CurrentExecutionContext != null, "Should either be acquiring or executing in Prepare."); this.pendingPreparingEnlistment = preparingEnlistment; } else { prepareNeeded = true; } } if (prepareNeeded) { preparingEnlistment.Prepared(); } } void OnRollBack(AcquireContextAsyncResult rollingBack) { bool rollbackNeeded = false; lock (ThisLock) { TooLateToEnlist = true; if (OperationPending && AcquirePending == null) { Fx.Assert(CurrentExecutionContext != null, "Should either be acquiring or executing in RollBack."); this.pendingRollback = rollingBack; // Don't prepare and roll back. this.pendingPreparingEnlistment = null; } else { rollbackNeeded = true; } } if (rollbackNeeded) { rollingBack.RollBack(); } } void FinishOperation() { List handlesPendingResolution = null; try { bool needNotification; PreparingEnlistment preparingEnlistment; AcquireContextAsyncResult pendingRollback; lock (ThisLock) { OperationPending = false; AcquirePending = null; CurrentExecutionContext = null; // This means we could have bound the handle, but didn't - clear the state here. if (this.inProgressBind != null && (Version == -1 || !IsValid)) { Owner.CancelBind(ref this.inProgressBind, ref handlesPendingResolution); } else if (Version != -1 && !IsValid) { // This means the handle was successfully bound in the past. Need to remove it from the table of handles. Owner.Unbind(this); } needNotification = this.needFreedNotification; this.needFreedNotification = false; preparingEnlistment = this.pendingPreparingEnlistment; this.pendingPreparingEnlistment = null; pendingRollback = this.pendingRollback; this.pendingRollback = null; } try { if (needNotification) { Store.FreeInstanceHandle(this, ProviderObject); } } finally { if (pendingRollback != null) { Fx.Assert(preparingEnlistment == null, "Should not have both."); pendingRollback.RollBack(); } else if (preparingEnlistment != null) { preparingEnlistment.Prepared(); } } } finally { InstanceOwner.ResolveHandles(handlesPendingResolution); } } List StartWaiting(WaitForEventsAsyncResult result, IOThreadTimer timeoutTimer, TimeSpan timeout) { lock (ThisLock) { if (this.waitResult != null) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.WaitAlreadyInProgress)); } if (!IsValid) { throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed)); } if (this.boundOwnerEvents != null && this.boundOwnerEvents.Count > 0) { Fx.Assert(Owner != null, "How do we have owner events without an owner."); List readyEvents = Store.SelectSignaledEvents(this.boundOwnerEvents, Owner); if (readyEvents != null) { Fx.Assert(readyEvents.Count != 0, "Should not return a zero-length list."); return readyEvents; } } this.waitResult = result; // This is done here to be under the lock. That way it doesn't get canceled before it is set. if (timeoutTimer != null) { timeoutTimer.Set(timeout); } return null; } } bool CancelWaiting(WaitForEventsAsyncResult result) { lock (ThisLock) { Fx.Assert(result != null, "Null result passed to CancelWaiting."); if (!object.ReferenceEquals(this.waitResult, result)) { return false; } this.waitResult = null; return true; } } internal void EventReady(InstancePersistenceEvent persistenceEvent) { WaitForEventsAsyncResult resultToComplete = null; lock (ThisLock) { if (this.waitResult != null) { resultToComplete = this.waitResult; this.waitResult = null; } } if (resultToComplete != null) { resultToComplete.Signaled(persistenceEvent); } } internal static IAsyncResult BeginWaitForEvents(InstanceHandle handle, TimeSpan timeout, AsyncCallback callback, object state) { return new WaitForEventsAsyncResult(handle, timeout, callback, state); } internal static List EndWaitForEvents(IAsyncResult result) { return WaitForEventsAsyncResult.End(result); } class AcquireContextAsyncResult : AsyncResult, IEnlistmentNotification { static Action
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- BackgroundFormatInfo.cs
- HttpRequestCacheValidator.cs
- WrappedReader.cs
- HandledMouseEvent.cs
- ScrollData.cs
- View.cs
- PixelShader.cs
- DataBinder.cs
- ParallelDesigner.xaml.cs
- DrawToolTipEventArgs.cs
- DelegatingHeader.cs
- IntegerValidator.cs
- XmlChoiceIdentifierAttribute.cs
- AnnotationAuthorChangedEventArgs.cs
- BasicKeyConstraint.cs
- SingleAnimationUsingKeyFrames.cs
- ExtensionSimplifierMarkupObject.cs
- FixedDSBuilder.cs
- ConnectivityStatus.cs
- DragEvent.cs
- XmlAttributeAttribute.cs
- ProxyGenerationError.cs
- HttpGetClientProtocol.cs
- SimpleWorkerRequest.cs
- ReadContentAsBinaryHelper.cs
- ScaleTransform3D.cs
- ButtonPopupAdapter.cs
- XmlIlGenerator.cs
- HttpConfigurationContext.cs
- TrustManager.cs
- ToolStripMenuItemDesigner.cs
- ArithmeticException.cs
- GradientBrush.cs
- FunctionImportElement.cs
- LayoutTable.cs
- DataListGeneralPage.cs
- XPathParser.cs
- ISO2022Encoding.cs
- HtmlTitle.cs
- COM2ExtendedBrowsingHandler.cs
- XmlCharacterData.cs
- SchemaImporterExtension.cs
- DefaultShape.cs
- UnauthorizedAccessException.cs
- LinearKeyFrames.cs
- HttpConfigurationContext.cs
- ReflectionHelper.cs
- WebServiceFault.cs
- ContainerParagraph.cs
- EndSelectCardRequest.cs
- BinaryUtilClasses.cs
- ListViewItem.cs
- FrameworkContextData.cs
- SqlFacetAttribute.cs
- CanonicalXml.cs
- Events.cs
- HtmlTableCell.cs
- XmlElementCollection.cs
- HotSpot.cs
- QilPatternVisitor.cs
- StaticContext.cs
- EventLogInternal.cs
- CompilerWrapper.cs
- SqlConnectionHelper.cs
- DuplicateContext.cs
- Mouse.cs
- ResourceDescriptionAttribute.cs
- ParameterElement.cs
- StringValidator.cs
- DataGridViewImageCell.cs
- ChildTable.cs
- LayoutEvent.cs
- CodeMemberMethod.cs
- MemoryStream.cs
- WebPartTransformerAttribute.cs
- FlatButtonAppearance.cs
- XmlFormatExtensionPointAttribute.cs
- PreviousTrackingServiceAttribute.cs
- MenuItemCollection.cs
- TemplateBuilder.cs
- VectorAnimation.cs
- EnumConverter.cs
- RegistrySecurity.cs
- XPathException.cs
- PathStreamGeometryContext.cs
- _NTAuthentication.cs
- Dump.cs
- EncodingNLS.cs
- EnvelopeVersion.cs
- WebBrowserProgressChangedEventHandler.cs
- _SslState.cs
- login.cs
- PageSettings.cs
- TableDetailsRow.cs
- FixedSOMLineCollection.cs
- ClientOptions.cs
- DataGridViewAddColumnDialog.cs
- RotateTransform3D.cs
- CalendarData.cs
- InvalidPipelineStoreException.cs