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
- FamilyTypeface.cs
- ValidatingPropertiesEventArgs.cs
- TheQuery.cs
- FastEncoderStatics.cs
- MouseBinding.cs
- BmpBitmapDecoder.cs
- TempFiles.cs
- ProgressBarRenderer.cs
- PaintValueEventArgs.cs
- DataGridViewAutoSizeModeEventArgs.cs
- DetailsViewUpdatedEventArgs.cs
- TableLayoutPanel.cs
- InputReportEventArgs.cs
- HttpPostedFile.cs
- Itemizer.cs
- UntrustedRecipientException.cs
- SafeThreadHandle.cs
- DebugView.cs
- QueryOutputWriter.cs
- GeneralTransform2DTo3DTo2D.cs
- SpeakInfo.cs
- SoapObjectInfo.cs
- DBSchemaRow.cs
- QilVisitor.cs
- followingquery.cs
- ProcessHost.cs
- DecimalConverter.cs
- dbdatarecord.cs
- MultiBinding.cs
- FormsAuthenticationModule.cs
- HwndTarget.cs
- PopOutPanel.cs
- XmlIlTypeHelper.cs
- TrackingProfile.cs
- ToolStripComboBox.cs
- SoapSchemaImporter.cs
- SecurityElement.cs
- TypeHelpers.cs
- CreateUserErrorEventArgs.cs
- CroppedBitmap.cs
- GridView.cs
- DataGridViewBand.cs
- CodeMemberMethod.cs
- LazyLoadBehavior.cs
- TraversalRequest.cs
- MenuItemAutomationPeer.cs
- TreeNodeEventArgs.cs
- StringAnimationUsingKeyFrames.cs
- BitmapImage.cs
- ErrorStyle.cs
- XmlHierarchyData.cs
- RepeatButton.cs
- InputReport.cs
- SqlNamer.cs
- UnmanagedMemoryAccessor.cs
- ListItemsCollectionEditor.cs
- DirectoryInfo.cs
- SelectedDatesCollection.cs
- JoinQueryOperator.cs
- SqlDataSourceStatusEventArgs.cs
- XsltInput.cs
- XhtmlConformanceSection.cs
- SoapTransportImporter.cs
- Vars.cs
- HttpApplication.cs
- AppModelKnownContentFactory.cs
- DefaultPrintController.cs
- SharedUtils.cs
- QilPatternVisitor.cs
- PasswordTextContainer.cs
- ExternalDataExchangeClient.cs
- LockCookie.cs
- RemoteWebConfigurationHost.cs
- Helpers.cs
- HtmlString.cs
- TimelineGroup.cs
- PcmConverter.cs
- ProfilePropertySettingsCollection.cs
- TableDetailsRow.cs
- ListBoxItemWrapperAutomationPeer.cs
- ConnectionConsumerAttribute.cs
- WebRequest.cs
- TailCallAnalyzer.cs
- NavigationWindow.cs
- Point3DCollectionConverter.cs
- ExceptionWrapper.cs
- EntityDataSourceConfigureObjectContextPanel.cs
- FieldToken.cs
- PlaceHolder.cs
- StackOverflowException.cs
- RangeContentEnumerator.cs
- TileModeValidation.cs
- DescendentsWalker.cs
- SmtpSection.cs
- Asn1IntegerConverter.cs
- FormatterServices.cs
- DocumentViewerAutomationPeer.cs
- DataGridrowEditEndingEventArgs.cs
- ConstrainedDataObject.cs
- TreeView.cs